Cover V03, I01
Article

jan94.tar


New Messages

Dear Mr. Ward,
A statement was made in the Question and Answer section of your November/December issue which I believe is unfounded.

On page 28, Bjorn Satdeva says, regarding Job Accounting software packages, "the few commercial packages I have come across have been buggy and cumbersome to deal with."

I think your readers should know that our resource accounting package is neither of those things. We have many satisfied customers using UNISOL JobAcct. Furthermore, many customers call us because they have tried to write their own system accounting packages and have run into difficulties. The biggest complaint we hear about other commercial packages concerns performance, which is not a problem with UNISOL JobAcct.

Sincerely,
Laurie Craig
Director of Marketing
UniSolutions Associates
Redondo Beach, CA 90278

Bjorn's comments reflected his own experience, but should not be read to imply that all commercial packages are subject to the same criticism. Thanks for writing to us.

Editor,
I saw the letter from Guido Beijerwellen in your November/December 1993 issue in which he complains about the availability of the sysadmin files via your telephone number.

You suggest CompuServe, but did you know that uunet keeps copies too? ftp to ftp.uu.net, go to the directory /published/sysadmin, and there you will find the files for 1992 and 1993 in the directories 1992 and 1993.

Other sites where the files are stored can be found using archie. I found the following places:
ftp.cs.umn.edu:/pub/doc/published/sysadmin

ftp.uu.net:/published/sysadmin

ftp.ccu.edu.tw:/pub1/document/published/sysadmin

nctuccca.edu.tw:/documents/published/sysadmin

unix.hensa.ac.uk:/pub/uunet/published/sysadmin

src.doc.ic.ac.uk:/media/literary/published/sysadmin

For those close to the Netherlands, we keep a mirror of some of the published documents on our archive server ftp.nl.net, in the directory /pub/published. This server can also be reached via our mail server: send mail to mail-server@nl.net and you will get help from the mailserver.

Since this would be the easiest way fo Mr. Beijerwellen to obtain the files, I have sent him a copy of this e-mail.

Jan Christiaan van Winkel
archive maintainer jc@inter.nl.net
Alternative e-mail addresses: jc@oreo.atcmp.nl and jc@atcmp.nl

Many thanks to you for this useful information. Readers unfamiliar with "archie" can look to Chris Bush's article, "Internet Online Services: archie," beginning on p. 37 of this issue, for an explanation.

Dear SysAdm:
Nice magazine, I enjoy reading it.

I have a question though:

Does anybody at SysAdm know where I may obtain information about the CM University Mach project and also about the GNU or Free Software Foundation Hurd project? I have very little info on either, all of it coming from my GNU magazine.

I appreciate your time and effort.

Thank you.
Ennis McCaffrey
emccaff@pica.army.mil

We're not able to provide this information, but feel sure that our readers can. If you can help Mr. McCaffrey out, e-mail him at the address above.

Editor:
A curious reader not to long ago sent me a question regarding UUCP and alarm messages. Before I reply to this forum with a general response, I am asking that individual to re-send his message to me. You can reach me via e-mail at chare@unilabs.org, or chare@choreo.ca.

Thanks.
Chris Hare
chare@unilabs.org

If you had this question for Chris, please contact him again.

Sys Admin Editor,
One of the great things about system administration is that there can be so many ways to solve a problem! In your checkcron script from Nov/Dec 1993, you have the following line:

ps -fu root | sed '/grep/d' | grep cron

For those who are not as familiar with using sed, the following line should accomplish the same thing:

ps -fu root | grep cron | grep -v grep

Here I use the -v option of grep to remove any lines containing grep. Again, due to the way the pipeline works, the second grep not only filters out the search for cron, but itself as well!

Jason Vogel
System Administrator
Sunbelt Business Computers
jason@sunbelt.com

Thanks for the elaboration -- the greater thing about system administration is system administrators' willingness to share such tips with each other!

From: Chris Linstruth <cjl@aop.com>
Subject: New Messages November/December 1993
To: saletter@rdpub.com

A reader queried about VMS-like virtual terminals in this issue [Nov/Dec 1993]. I think the freeware 'screen' package from the FSF should fill their needs...

It allows multiple sessions to be run (each on a pty) on regular ASCII terminals (vt100 works best). Screen can be configured to not drop these sessions when it terminates so they can be reattached at a later time. It works like a champ. I don't know of an ftp site but I'm sure it's on all the major GNU archives.

Here's what it looks like.

$ screen -?
Use: screen [-opts] [cmd [args]]
or: screen -r [host.tty]

Options:

-a -- Force all capabilities into each window's termcap.

-A -[r|R] -- Adapt all windows to the new display width & height.

-c file -- Read configuration file instead of '.screenrc'.

-d (-r) -- Detach the elsewhere running screen (and reattach here).

-D (-r) -- Detach and logout remote (and reattach here).

-e xy -- Change command characters.

-f -- Flow control on, -fn = off, -fa = auto.

-h -- lines Set the size of the scrollback history buffer.

-i -- Interrupt output sooner when flow control is on.

-l -- Login mode on (update /etc/utmp), -ln = off.

-list or -ls. -- Do nothing, just list our SockDir.

-L -- Terminal's last character can be safely updated.

-m -- ignore $STY variable, do create a new screen session.

-O -- Choose optimal output rather than exact vt100 emulation.

-q -- Quiet startup. Exits with non-zero return code if unsuccessful.

-r -- Reattach to a detached screen process.

-R -- Reattach if possible, otherwise start a new session.

-s -- shell Shell to execute rather than $SHELL.

-S sockname -- Name this session <pid>.sockname instead of <pid>.<tty>.<host>.

-t title -- Set command's a.k.a. (window title).

-T term -- Use term as $TERM for windows, rather than "screen".

-v -- Print "Screen version 3.05.00 (FAU) 22-Jul-93".

-wipe -- Do nothing, just clean up SockDir.

-x -- Attach to a not detached screen. (Multi display mode).

Chris Linstruth
Capitol Heights, Maryland
cjl@aop.com

This is one I'm not familiar with -- let's hope it does the trick.

Editor:
In your November/December 1993 issue of Sys Admin, you printed an article by Don Pipkin titled "A Revised sudo: Managing Super-User Privileges."

I ftp-ed the source code from this article and was mildly disappointed when it failed to work on our Pyramid MIServer-ES (running SVR4). A cursory examination of the code showed that it was not using the function calls that were created to handle the shadow password file (/etc/shadow). I asked a colleague, Paul Ready, to add in the corresponding functions, which he did. With those changes, together with a couple of minor changes elsewhere, the code now compiles and runs successfully on our Pyramid box.

The code that Paul added tests the length of the password field in /etc/passwd and, if it is not 13 chars long, calls the shadow password functions. So this code works with both shadow and non-shadow passwords.

We have tested in on both the Pyramid (1.1-93c062 dcosx MIServer-ES 4/256 r3000) and under SCO Xenix 2.3.4.

Because of the changes from tabs to spaces and back again over the many transfers this source has had, the 'diff' between the two versions does not help too much!, so, here is the whole source.

Ben Thomas, P.Eng
ben@ShoppersDrugMart.CA

Thanks for taking the time to investigate and solve the problem. Interested readers will find the code in below.

Listing 1: new sudo.c, revised by Ben Thomas and Paul Ready

/*
**   sudo.c
*/

/*
**   Configurable constants
*/

#define SECURE_PATH
"/bin:/usr/sbin:/sbin:/usr/lbin:/opt/x25/bin/usr/bin/X11"
#define USERFILE    "/usr/local/adm/sudoers"
#define LOGFILE     "/usr/local/adm/sudolog"
#define TIMEDIR     "/usr/local/adm/sudocheck/"
#define TIME         5 /* minutes */
#define SUDO_COP    "root@localhost"

/*
**   To install create the directories in the SECURE_PATH
**   and TIMEDIR and the files USERFILE and LOGFILE.
**   Set the owner of these files to root and the permissions
**   to og-rw.  Change the owner to root for the sudo program
**   and set the permissions to 4111 (--s--x--x).
*/

#define BUFSIZE  256
#define SUCCESS  1
#define FAILED  -1
#define CONFIG_ERR "Configuration error.  " \
"Contact your security manager."
#define CONFIG_SUB "Invalid sudo configuration!"
#define SUID_ERR   "Set UID bit not set."
#define STAT_ERR   "Couldn't stat user file"
#define OWNER_ERR  "User file not owned by root"
#define PERM_ERR   "Invalid permissions on user file"
#define OPEN_ERR   "Couldn't open user file"
#define USER_ERR   "You are not set up to execute " \
"sudo on this machine."
#define USER_SUB   "SECURITY**  Invalid sudo user!"

#include <ctype.h>
#include <fcntl.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <sys/file.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <grp.h>
#include <shadow.h>

char *getpass();
char *crypt();
char *find_passwd(int user_id);

FILE *check_config(user)
char *user;
{

struct stat statb;
FILE *fp;
if ((setuid(0)) < 0) /* no setuid */
mail(CONFIG_SUB, user, SUID_ERR, CONFIG_ERR);
if (stat(USERFILE, &statb))   /* couldn't stat */
mail(CONFIG_SUB, user, STAT_ERR, CONFIG_ERR);
if (statb.st_uid != 0) /* must be owned by root */
mail(CONFIG_SUB, user, OWNER_ERR, CONFIG_ERR);
if (statb.st_mode & 044)   /* should be og-rw */
mail(CONFIG_SUB, user, PERM_ERR, CONFIG_ERR);
if ((fp = fopen(USERFILE, "r")) == 0) /* open err */
mail(CONFIG_SUB, user, OPEN_ERR, CONFIG_ERR);
return(fp);
}

char *get_command_list(fp, name, password)
FILE *fp; char *name; char *password;
{
char buffer[BUFSIZ];
while ((fgets(buffer, BUFSIZ, fp)) != NULL) {
if (buffer[0] == '#') continue; /* skip remarks */

if ((strncmp(buffer, name, strlen(name))) == 0)
if ((get_password(name, password)) == SUCCESS)
return(buffer); /* valid user */
else
return(NULL); /* invalid password */
}
return(NULL); /* invalid user */
}

int get_password(name, password)
char *name; char *password;
{
char     fname[200];
char     *paswd;
char     *encrypted;
struct   stat stab;
sprintf (fname, "%s/%s", TIMEDIR, name);
if (stat(fname, &stab) == 0) {
if ((time(NULL) - stab.st_mtime) < (TIME * 60)) {
create_file(fname);
return (SUCCESS);
}
}

paswd = getpass("Password:");
encrypted = crypt(paswd, password);
if (strcmp(password, encrypted)) {
fprintf (stderr, "Password incorrect\n");
return(FAILED);
}

fflush (stderr);
fflush (stdout);
create_file(fname);
return (SUCCESS);
}

create_file(file)
char *file;
{
int      descrip;
long     timep[2];
if ((descrip = open(file, O_TRUNC|O_CREAT|O_WRONLY,  0700)) > 0) {
close (descrip);
timep[0] = timep[1] = time(0);
utime (file, timep);
}
}

int checkdoer (dp, ap)
char *dp; char *ap;
{

char    *cp0, *cp1;
int not_flag = 0;
cp0 = dp;
while (isalnum (*cp0))
cp0++;  /* skip past user */
while (*cp0) {   /* search until end of line */
while (isspace (*cp0))
cp0++;   /* skip to cmd */
if (strncmp (cp0, "not", 3) == 0)
not_flag = 1;
if (strncmp (cp0, "all", 3) == 0)
return (SUCCESS);
cp1 = cp0;

/* find end of this entry */
while (*cp1 != '\n' && !isspace(*cp1))
cp1++;
if (strncmp (cp0, ap, strlen(ap)) == 0) {
cp1 = '\0';
if (not_flag)
return(FAILED);
else
return(SUCCESS);
}

/* move pointer past and keep looking */
while (!isspace (*cp0) && *cp0 != '\n')
cp0++;
if (*cp0 == '\n')
break;  /* if EOL then fail */
else
continue;
}

if (not_flag)
return(SUCCESS);
else
return(FAILED);
}

char *log(username, info, argc, argv)
char *username; char *info; int argc; char **argv;
{
FILE *fp;
long now;
char ret_st[BUFSIZ];
if ((fp = fopen(LOGFILE, "a")) == NULL)
mail("WARNING sudo can't open logfile.", username,  LOGFILE,
"");
now = time((long*) 0);
fprintf (fp, "%20.20s :", ctime(&now));
fprintf (fp, "%10.10s", info);
fprintf (fp, "%9.9s :",username);
while (argc--) {
fprintf (fp, "%s ", *argv++);
sprintf (ret_st, "%s ", *argv++);
}
fprintf (fp, "\n");
(void) fclose (fp);
return(ret_st);
}

int mail(subject, user, text, err_msg)
char *subject; char *user; char *text; char *err_msg;
{
char hostname[MAXHOSTNAMELEN];
FILE *fd;
FILE *popen();
char cmd[80];
firsthostname (hostname, MAXHOSTNAMELEN);
(void) sprintf (cmd, "/usr/bin/mailx  -s \"%s\" %s ", subject,
SUDO_COP);
if ((fd = popen (cmd, "w")) == NULL)
return;
(void) fputs(text, fd);
(void) fputs ("\n\nThought you might want to know.", fd);
(void) pclose(fd);
fprintf(stderr, "%s %s", err_msg, "\n");
exit(1);
}

int firsthostname(n, l)
char *n; int l;
{
(void) gethostname(n, l); /* get full hostname */
n[l - 1] = 0;         /* make sure null terminated */
if (n = strchr(n,  '.'))
*n = 0; /* put null on '.'*/
}

int main(int argc, char **argv)
{
FILE *userfile;
struct passwd *user;
long uid;
char *valid_cmds;
char *progname;
char *cmd_line;
char *real_passwd;
progname = argv[0];
if (argc < 2) {
fprintf(stderr, "usage: %s cmd\n", progname);
exit(1);
}

uid = getuid();
user = getpwuid(uid);
userfile = check_config(user);
real_passwd = find_passwd(uid);
if ((valid_cmds = get_command_list(userfile, user->pw_name,
real_passwd)) != NULL) {
argv++, argc--;
if (checkdoer(valid_cmds, *argv) != FAILED) {
putenv("PATH=" SECURE_PATH);
putenv("SHELL=/bin/false");
(void) log(user->pw_name, "", argc, argv);
execvp(*argv, argv);   /* then do it */
perror(*argv);
} else {
(void) log(user->pw_name, "FAIL ", argc,  argv);
fprintf(stderr, "%s: You are not set up to "
"execute %s with sudo on this machine.\n",
progname, *argv);
exit(1);
}
} else {
cmd_line = log(user->pw_name, "FAIL ", argc, argv);
mail(USER_SUB, user->pw_name, cmd_line, USER_ERR);
}
}

char *find_passwd(int user_id)
{
struct passwd *pw, *getpwuid();
struct group  *gr, *getgrgid();
struct spwd *spw, *getspnam();
if ((pw = getpwuid(user_id)) == NULL)
return("unknown");
if (strlen(pw->pw_passwd) == 13) {
if ((pw = getpwuid(user_id)) == NULL) {
return("unknown");
} else {
return(pw->pw_passwd);
}
} else {
if ((spw = getspnam(pw->pw_name)) == NULL) {
return("unknown");
} else {
return(spw->sp_pwdp);
}
}
return("unknown");
}