dce@mips.UUCP (02/04/87)
Apply the following to the 4.3BSD su.c using patch: ---------------------------- *** su.c.old Wed Feb 4 08:06:53 1987 --- su.c Wed Feb 4 08:08:23 1987 *************** *** 17,26 **** --- 17,29 ---- #include <stdio.h> #include <pwd.h> #include <grp.h> + #include <string.h> #include <syslog.h> #include <sys/types.h> #include <sys/time.h> #include <sys/resource.h> + #include <sys/stat.h> + #include <sys/param.h> char userbuf[16] = "USER="; char homebuf[128] = "HOME="; *************** *** 28,41 **** char pathbuf[128] = "PATH=:/usr/ucb:/bin:/usr/bin"; char *cleanenv[] = { userbuf, homebuf, shellbuf, pathbuf, 0, 0 }; char *user = "root"; ! char *shell = "/bin/sh"; int fulllogin; int fastlogin; extern char **environ; struct passwd *pwd; char *crypt(); char *getpass(); char *getenv(); char *getlogin(); --- 31,77 ---- char pathbuf[128] = "PATH=:/usr/ucb:/bin:/usr/bin"; char *cleanenv[] = { userbuf, homebuf, shellbuf, pathbuf, 0, 0 }; char *user = "root"; ! ! #define DEFAULT_SHELL "/bin/sh" ! char *shell = DEFAULT_SHELL; int fulllogin; int fastlogin; + /* + * New flags: + * + * -e Do not reset the environment. + * -c Execute extra arguments directly as command instead of using + * shell. If none are given, use the shell. + * + * If su is called as 'ssu', it is the same as 'su -e -c root'. + * + */ + + int Reset_env = 1; + int Command = 0; + int Have_user = 0; + + /* + * New feature: + * + * If the user is trying to su to root, the file /etc/su_people is read + * to see if the user is in it. If so, no password is required. + * + * This file MUST have owner and group 0 and mode 600 to be read, and no + * messages are printed if it isn't. This avoids someone accidentally + * leaving the file in such a state that users can change it and gain + * free system access. + */ + + #define FREE_FILE "/etc/su_people" + int ck_free_entry(); + extern char **environ; struct passwd *pwd; char *crypt(); char *getpass(); + char *get_enveq(); char *getenv(); char *getlogin(); *************** *** 44,67 **** char *argv[]; { char *password; ! char buf[1000]; FILE *fp; ! register char *p; openlog("su", LOG_ODELAY, LOG_AUTH); ! again: ! if (argc > 1 && strcmp(argv[1], "-f") == 0) { ! fastlogin++; ! argc--, argv++; ! goto again; } ! if (argc > 1 && strcmp(argv[1], "-") == 0) { ! fulllogin++; ! argc--, argv++; ! goto again; ! } ! if (argc > 1 && argv[1][0] != '-') { user = argv[1]; argc--, argv++; } --- 80,125 ---- char *argv[]; { char *password; ! char name[1000]; FILE *fp; ! int su_for_free; ! register char *mylogin; openlog("su", LOG_ODELAY, LOG_AUTH); ! ckname(argv[0]); /* Set flags based on name */ ! ! while (argc > 1 && argv[1][0] == '-') { ! switch (argv[1][1]) { ! ! case 'f': ! fastlogin = 1; ! break; ! ! case '\0': ! fulllogin = 1; ! break; ! ! case 'e': ! Reset_env = 0; ! break; ! ! case 'c': ! Command = 1; ! break; ! ! default: ! fprintf(stderr, "su: usage: su [-e] [-f] [-c] [-] [user] ..."); ! exit(1); ! break; ! ! } ! argv++; ! argc--; } ! ! ! if (!Have_user && argc > 1 && argv[1][0] != '-') { user = argv[1]; argc--, argv++; } *************** *** 69,89 **** fprintf(stderr, "Who are you?\n"); exit(1); } ! strcpy(buf, pwd->pw_name); if ((pwd = getpwnam(user)) == NULL) { fprintf(stderr, "Unknown login: %s\n", user); exit(1); } /* ! * Only allow those in group zero to su to root. */ ! if (pwd->pw_uid == 0) { struct group *gr; int i; if ((gr = getgrgid(0)) != NULL) { for (i = 0; gr->gr_mem[i] != NULL; i++) ! if (strcmp(buf, gr->gr_mem[i]) == 0) goto userok; fprintf(stderr, "You do not have permission to su %s\n", user); --- 127,159 ---- fprintf(stderr, "Who are you?\n"); exit(1); } ! strcpy(name, pwd->pw_name); if ((pwd = getpwnam(user)) == NULL) { fprintf(stderr, "Unknown login: %s\n", user); exit(1); } /* ! * Only allow those in group zero or in the su_people file to su to ! * root, unless the effective groupid is 0 (i.e., if the command ! * is setgid 0). */ ! ! if ((mylogin = getlogin()) == NULL || *mylogin == '\0') { ! mylogin = name; ! } ! if (pwd->pw_uid == 0 && ck_free_entry(mylogin, name)) { ! su_for_free = 1; ! } else { ! su_for_free == 0; ! } ! ! if (!su_for_free && pwd->pw_uid == 0 && getegid() != 0) { struct group *gr; int i; if ((gr = getgrgid(0)) != NULL) { for (i = 0; gr->gr_mem[i] != NULL; i++) ! if (strcmp(name, gr->gr_mem[i]) == 0) goto userok; fprintf(stderr, "You do not have permission to su %s\n", user); *************** *** 93,114 **** setpriority(PRIO_PROCESS, 0, -2); } ! #define Getlogin() (((p = getlogin()) && *p) ? p : buf) ! if (pwd->pw_passwd[0] == '\0' || getuid() == 0) ! goto ok; ! password = getpass("Password:"); ! if (strcmp(pwd->pw_passwd, crypt(password, pwd->pw_passwd)) != 0) { ! fprintf(stderr, "Sorry\n"); ! if (pwd->pw_uid == 0) { ! syslog(LOG_CRIT, "BAD SU %s on %s", ! Getlogin(), ttyname(2)); } - exit(2); } - ok: endpwent(); if (pwd->pw_uid == 0) { ! syslog(LOG_NOTICE, "%s on %s", Getlogin(), ttyname(2)); closelog(); } if (setgid(pwd->pw_gid) < 0) { --- 163,182 ---- setpriority(PRIO_PROCESS, 0, -2); } ! if (pwd->pw_passwd[0] != '\0' && getuid() != 0 && !su_for_free) { ! password = getpass("Password:"); ! if (strcmp(pwd->pw_passwd, crypt(password, pwd->pw_passwd)) != 0) { ! fprintf(stderr, "Sorry\n"); ! if (pwd->pw_uid == 0) { ! syslog(LOG_CRIT, "BAD SU %s on %s", ! mylogin, ttyname(2)); ! } ! exit(2); } } endpwent(); if (pwd->pw_uid == 0) { ! syslog(LOG_NOTICE, "%s on %s", mylogin, ttyname(2)); closelog(); } if (setgid(pwd->pw_gid) < 0) { *************** *** 123,139 **** perror("su: setuid"); exit(5); } ! if (pwd->pw_shell && *pwd->pw_shell) ! shell = pwd->pw_shell; if (fulllogin) { ! cleanenv[4] = getenv("TERM"); environ = cleanenv; } ! if (strcmp(user, "root")) ! setenv("USER", pwd->pw_name, userbuf); ! setenv("SHELL", shell, shellbuf); ! setenv("HOME", pwd->pw_dir, homebuf); setpriority(PRIO_PROCESS, 0, 0); if (fastlogin) { *argv-- = "-f"; *argv = "su"; --- 191,222 ---- perror("su: setuid"); exit(5); } ! if (Reset_env) { ! if (pwd->pw_shell && *pwd->pw_shell) { ! shell = pwd->pw_shell; ! } ! } else { ! shell = getenv("SHELL"); ! if (shell == NULL) { ! shell = DEFAULT_SHELL; ! } ! } if (fulllogin) { ! cleanenv[4] = get_enveq("TERM"); environ = cleanenv; } ! if (Reset_env) { ! if (strcmp(user, "root")) ! setenv("USER", pwd->pw_name, userbuf); ! setenv("SHELL", shell, shellbuf); ! setenv("HOME", pwd->pw_dir, homebuf); ! } setpriority(PRIO_PROCESS, 0, 0); + if (Command && argc > 1) { + execvp(argv[1], argv + 1); + fprintf(stderr, "Could not execute %s\n", argv[1]); + exit(7); + } if (fastlogin) { *argv-- = "-f"; *argv = "su"; *************** *** 170,177 **** } } char * ! getenv(ename) char *ename; { register char *cp, *dp; --- 253,269 ---- } } + /* + * The subroutine get_enveq() was called getenv(), but this isn't a + * good idea, since it may cause profiling to break. Also, we need the + * normal getenv behavior, too. + * + * This routine returns the full "NAME=value" string instead of just the + * value portion. + */ + char * ! get_enveq(ename) char *ename; { register char *cp, *dp; *************** *** 184,187 **** --- 276,541 ---- return (*--ep); } return ((char *)0); + } + + /* + * The subroutine ckname() sets options based on the name of the command. + */ + + ckname(name) + char *name; + { + char *base; + + base = rindex(name, '/'); + if (base) { + base++; + } else { + base = name; + } + + if (strcmp(base, "ssu") == 0) { + Have_user = 1; + Reset_env = 0; + Command = 1; + } + } + + /* + * The subroutine ck_free_entry() checks to see if the user need not give + * a password. This is only true if the file /etc/su_people is mode 0600, + * has owner and group 0, and contains one of the given names. We look up + * both the current username and the login name, since the user could + * already be su'ed. + * + * The return value is 1 if the above are true, and 0 if not. + */ + + static int ck_apply(); + #define A_NOT 0 + #define A_ALLOW 1 + #define A_DENY 2 + + ck_free_entry(name1, name2) + char *name1; + char *name2; + { + + struct stat statb; + FILE *fp; + char buf[1024]; /* Buffer for holding data */ + + if (name1 == NULL || name1[0] == '\0' || + name2 == NULL || name2[0] == '\0') { /* Unsafe */ + return 0; + } + + if (stat(FREE_FILE, &statb) < 0) { + return 0; + } + + if ((statb.st_mode & ~S_IFMT) != 0600 || statb.st_uid != 0 || + statb.st_gid != 0) { + return 0; + } + + if ((fp = fopen(FREE_FILE, "r")) == NULL) { + return 0; + } + + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (buf[strlen(buf) - 1] != '\n') { /* Line too long */ + (void) fclose(fp); + return 0; + } + if (buf[0] == '#') { /* Comment */ + continue; + } + + switch(ck_apply(name1, buf)) { + case A_ALLOW: + (void) fclose(fp); + return 1; + + case A_DENY: + (void) fclose(fp); + return 0; + } + + if (strcmp(name1, name2) == 0) { + continue; + } + switch(ck_apply(name2, buf)) { + case A_ALLOW: + (void) fclose(fp); + return 1; + + case A_DENY: + (void) fclose(fp); + return 0; + } + } + + (void) fclose(fp); + return 0; + } + + /* + * The subroutine ck_apply() checks to see if the buffer applies to the + * given name. If not, A_NOT is returned. Otherwise, the buffer is checked + * to see how it applies, based on the buffer type. + * + * The buffer may be one of the following types: + * + * {name} + * Return A_ALLOW if either of the names is {name} + * {name} {hostname list} + * Return A_ALLOW if either of the names is {name} and the + * current hostname is in {hostname list} + * {name} !{hostname list} + * Return A_DENY if either of the names is {name} and the + * current hostname is in {hostname list} + * + * The hostname list is a list of names separated by commas or whitespace. + */ + + static int ck_host(); + + static int + ck_apply(name, buf) + char *name; + char *buf; + { + + int len; /* Length of name */ + + len = strlen(name); + if (strncmp(name, buf, len) == 0) { + switch(buf[len]) { + case '\n': + return A_ALLOW; + break; + + case '\t': + case ' ': + if (ck_empty(&buf[len + 1])) { + return A_ALLOW; + } + return ck_host(&buf[len + 1]); + break; + } + + /* + * Name doesn't match. May not be a syntax error. Example: + * name = foo + * buffer = foobar + */ + } + + return A_NOT; + } + + /* + * The subroutine ck_empty() returns 1 if the given string contains only + * spaces and tabs followed by a newline, and 0 otherwise. + */ + + static + ck_empty(str) + char *str; + { + + while (*str != '\n') { + if (*str != ' ' && *str != '\t') { + return 0; + } + str++; + } + + return 1; + } + + /* + * The subroutine ck_host() takes a list of hostnames and checks to see + * if the current hostname is in the list. + * + * If the current hostname is in the list, A_ALLOW is returned. If not, + * A_DENY is returned. If the list begins with '!', these two values are + * reversed. + * + * If there are any syntax errors, A_DENY is returned for the sake of safety. + */ + + static int + ck_host(list) + char *list; + { + + int not = 0; /* 1 if list begins with ! */ + static char host[MAXHOSTNAMELEN + 1]; /* Current hostname */ + static int hlen = 0; /* Length of hostname */ + + if (hlen == 0) { + gethostname(host, MAXHOSTNAMELEN); + hlen = strlen(host); + } + + /* + * Find beginning of list. + */ + + while (*list == ' ' || *list == '\t') { + list++; + } + + if (*list == '!') { + not = 1; + list++; + while (*list == ' ' || *list == '\t') { + list++; + } + if (*list == '\n') { + return A_DENY; /* Syntax */ + } + } + + /* + * Look at each list element + */ + + while (*list != '\n') { + if (strncmp(list, host, hlen) == 0) { + switch (list[hlen]) { + case ' ': + case ',': + case '\t': + case '\n': + if (not) { + return A_DENY; + } else { + return A_ALLOW; + } + break; + } + } + + /* + * Go to next list item. + */ + + list++; + while (*list != ' ' && *list != '\t' && *list != ',' && + *list != '\n') { + list++; + } + while (*list == ' ' || *list == '\t' || *list == ',') { + list++; + } + } + + if (not) { + return A_ALLOW; + } else { + return A_DENY; + } }