Modifications to 4.3BSD su - part 1 (su diffs)
dce at mips.UUCP
dce at mips.UUCP
Thu Feb 5 02:51:30 AEST 1987
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;
+ }
}
More information about the Comp.sources.unix
mailing list