Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!cs.utexas.edu!tut.cis.ohio-state.edu!ucbvax!hplabs!hpfcso!hplisa!hpislx!larryc From: larryc@hpislx.HP.COM (Larry Corsa) Newsgroups: comp.sys.hp Subject: Re: How can I give users root-like privs. w/o the passwd ? Message-ID: <5820019@hpislx.HP.COM> Date: 2 Jan 90 23:23:34 GMT References: <2643@umbc3.UMBC.EDU> Organization: HP Measurement Systems Operation - Loveland, CO Lines: 286 > I've run into a bit of a snag, I'm sure other people have had to do > this, so here it goes. I would like to give some specific users root-like > privs. to execute some privaledge commands. But, I don't want to give > them the root passwd, I think that's too much. I specifically want to > give a user privaledge to mount a fs, and to halt (shutdown) the system in the > event of an emergency. I tried setting up a newgrp for these users, and > cvhanging the mod and group of the respective files. But it didn't work, ( or > I may have missed a file) ? Any suggestions ? > At the risk of disseminating info I only barely understand, I have attached source to a program called uid_chng , which takes as its argument a command which is to be executed with root permission. It has an access file which allows it to be discriminating in its choice of user and command access. I freely admit that its presence on any system is a likely security breach. ( I also admit to using it. :-) ) The source is reasonably self-documenting (which is to say I didn't get any documentation when I received it either!). Let it also be known that this is UNSUPPORTED by either myself or my employer! Regards, Larry Corsa | HPDesk address: HP0900/UX MSO Technical Product Support Engineer | HPUX address: larryc@hpisla Measurement Systems Operation | (303) or TELNET 679-5080 815 14th Street SW | FAX (303) or TELNET 679-5957 Mail Stop CU312 | Loveland, CO 80537, USA | ================== cut here =========================== /*******************************************************************************PROGRAM uid_chng.c PURPOSE Assume super user priveliges and execute a program ******************************************************************************** FUNCTIONAL DESCRIPTION Sets the process's user ID to 0 and exec's the passed shell program. NOTES Compile with: cc -n -O -o uid_chng uid_chng.c cc -n -O -DDEBUG -o uid_chng uid_chng.c # see what will be executed This program must be owned by root with suid bit set. chown root uid_chng chgrp other uid_chng chmod 4111 uid_chng ******************************************************************************/ #include #include char sccs_id[] = "@(#) uid_chng.c, version 1.9, dated 2/13/86"; /*************************************************************************************** ******** ******* main program ******* ******** ******** *******************************************************************************/ #define MAXCMD 256 /* maximum allowed length of command */ #define YES 1 #define NO 0 #define ROOT 0 /* super user ID */ extern int errno; char *getenv(); void main(argc,argv) int argc; char **argv; { unsigned short getuid(); /* returns user id */ /* | local data */ int i; char full_command[MAXCMD]; /* place for the command */ char user_shell[100]; /* $SHELL from environment */ /* | Must have at least one argument. Do this now so we don't try | and look for nothing in the ACCESS_FILE. */ if ( argc <= 1) { fprintf(stderr,"%s: No command given. \n", argv[0]); exit(1); } /* | if root is calling, then we need not verify priviliges nor | do we have to setuid(). */ if ( getuid() != ROOT ) { /* | make sure the user is allowed to call the requested command */ if ( idcmdok ( argv[1] ) != YES ) { fprintf(stderr,"%s: Invalid user and/or command.\n",argv[1]); exit(1); } /* | become super user */ #ifndef DEBUG if ( setuid(ROOT) == -1 ) { fprintf(stderr, "%s: The uid_chng command must be priviliged.\n", argv[0]); exit(1); } #else /* | debug is ON, just show message rather than setting uid */ fprintf(stderr,"Becoming super user\n"); #endif /* |end of "if not root" */ } /* | make the command */ strcpy(user_shell, getenv("SHELL=")); strcpy(full_command, " "); if (strcmp(user_shell,"/bin/sh") == 0 || strcmp(user_shell,"/bin/ksh") == 0 || strcmp(user_shell,"/bin/csh") == 0 ) { strcpy (full_command, user_shell); strcat (full_command, " -c \""); } strcat (full_command, argv[1]); /* command name */ /* | tack on arguments to the command */ for (i=2; i <= argc; i++) { strcat (full_command, " "); /* put in spaces... */ strcat (full_command, argv[i]); } strcat (full_command, "\""); #ifndef DEBUG /* | execute the command with its arguments */ if (system(full_command) != 0) { fprintf(stderr, "\n%s: System(2) error #%d\n", argv[0], errno); exit(1); } #else /* | print the argument and quit */ printf("\n%s\n",full_command); #endif /* | end of main program */ } /*************************************************************************************** ******** ******* idcmdok() ******* ******** ******** ******************************************************************************** idcmdok - id command ok This routine looks in the file ACCESS_FILE for the login user id. If found, the corresponding line is searched for the requested command. The format of the ACCESS_FILE is "user_id:command,command,....." "user_id:*" "*:command,command,..." where command is a valid allowable command. The "*" for command means this user has no restricted command. The "*" for user_id means the listed commands can be executed by any user. Also, tabs and spaces can be used to separate files to enhance readability (at the expense of security). (?) A user may be listed more than once. If the line starts with a # sign, that line is ignored (for comments). RETURNS: YES if command ok NO if command not allowed. BUGS: Note that /bin/rm is not the same as rm. If the system administrator entered "rm" in the ACCESS_FILE for a user to use, a clever user looking for trouble could make his own version of "rm" (say a shell script) and if path points to the new rm before /bin/rm then the user's rm would execute with root privileges! Therefore, ACCESS_FILE entries should have their entire pathname specified!!! ******************************************************************************/ #define ACCESS_FILE "/etc/su_access" #define MAXLINE 256 /* max length of ACCESS_FILE entry */ int idcmdok(cmdpt) char *cmdpt; /* points to routine that wants to be executed */ { /* | local data */ FILE *fp; /* file pointer for ACCESS_FILE */ FILE *fopen(); char *token; /* points to found token */ static char id_token[] = " :\n"; /* valid user_id delimiter */ static char cmd_token[] = " ,\n"; /* valid command delim */ char login_name[15]; char access_line[MAXLINE]; /* | prepare access file */ if ( (fp = fopen(ACCESS_FILE,"r") ) == NULL ) { fprintf(stderr,"error: Can't open access file '%s'.\n", ACCESS_FILE); exit(1); } /* | get the user's login id (name) */ if ( cuserid(login_name) == NULL ) { fprintf(stderr,"error: Can't get login name.\n"); exit(1); } /* | go through access file looking for the login name or a "*". | If found, parse the line to find the requested command. | If found, return YES; else, continue search until the | access file is exhausted. | Return NO if the proper match cannot be made. */ /* get a line */ while ( fgets(access_line,MAXLINE,fp) != NULL ) { /* look for comment line*/ if (access_line[0] == '#' ) continue; /* check for empty line */ if ( (token = strtok(access_line,id_token)) == (char*)0 ) continue; /* is the token found the login name or "*"? */ if ( strcmp(token,login_name) && strcmp(token,"*") ) continue; /* user id matches... now look for command match */ while ( (token = strtok((char*)NULL, cmd_token)) != (char*)NULL ) { /* if there is white space after the userid then | the next strok() will send us the ":" separator, which | we don't want. To address this case, */ if ( token[0] == ':') strcpy(token, &token[1]); /* if there is also whitespace after the ":" then the | above fix would allow a null command, which we also | don't want. */ if (token[0] == '\000') continue; if ((strcmp(token, cmdpt) == 0) || (strcmp(token,"*") == 0)) return (YES); } } /* | proper match could not be made */ return (NO); /******************************************************************************/ }