3 * $Id: become.c,v 1.6 1997/09/05 13:47:44 mdw Exp $
5 * Main code for `become'
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of `become'
14 * `Become' is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * `Become' is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with `become'; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 /*----- Revision history --------------------------------------------------*
32 * Revision 1.6 1997/09/05 13:47:44 mdw
33 * Make the `-L' (trace-level) option's argument optional, like the long
36 * Revision 1.5 1997/09/05 11:45:19 mdw
37 * Add support for different login styles, and environment variable
38 * manipulation in a safe and useful way.
40 * Revision 1.4 1997/08/20 16:15:13 mdw
41 * Overhaul of environment handling. Fix daft bug in path search code.
43 * Revision 1.3 1997/08/07 16:28:59 mdw
44 * Do something useful when users attempt to become themselves.
46 * Revision 1.2 1997/08/04 10:24:20 mdw
47 * Sources placed under CVS control.
49 * Revision 1.1 1997/07/21 13:47:54 mdw
54 /*----- Header files ------------------------------------------------------*/
56 /* --- ANSI headers --- */
65 /* --- Unix headers --- */
67 #include <sys/types.h>
69 #include <sys/socket.h>
70 #include <sys/utsname.h>
72 #include <netinet/in.h>
74 #include <arpa/inet.h>
81 extern char **environ
;
83 /* --- Local headers --- */
98 /*----- Type definitions --------------------------------------------------*/
100 /* --- Symbol table entry for an environment variable --- */
102 typedef struct sym_env
{
103 sym_base _base
; /* Symbol table information */
104 unsigned f
; /* Flags word (see below) */
105 char *val
; /* Pointer to variable value */
108 /* --- Environment variable flags --- */
114 /* --- Login behaviour types --- */
117 l_preserve
, /* Preserve the environment */
118 l_user
, /* Update who I am */
119 l_login
/* Do a full login */
122 /*----- Static variables --------------------------------------------------*/
124 static sym_table bc__env
;
126 /*----- Main code ---------------------------------------------------------*/
128 /* --- @bc__write@ --- *
130 * Arguments: @FILE *fp@ = pointer to a stream to write on
131 * @const char *p@ = pointer to a string
135 * Use: Writes the string to the stream, substituting the program
136 * name (as returned by @quis@) for each occurrence of the
140 static void bc__write(FILE *fp
, const char *p
)
142 const char *n
= quis();
144 size_t nl
= strlen(n
);
146 /* --- Try to be a little efficient --- *
148 * Gather up non-`$' characters using @strcspn@ and spew them out really
159 fwrite(n
, nl
, 1, fp
);
164 /* --- @bc__setenv@ --- *
166 * Arguments: @sym_env *e@ = pointer to environment variable block
167 * @const char *val@ = value to set
171 * Use: Sets an environment variable block to the right value.
174 static void bc__setenv(sym_env
*e
, const char *val
)
176 e
->val
= xmalloc(strlen(e
->_base
.name
) + 1 + strlen(val
) + 1);
177 sprintf(e
->val
, "%s=%s", e
->_base
.name
, val
);
180 /* --- @bc__putenv@ --- *
182 * Arguments: @const char *var@ = name of the variable to set, or 0 if
183 * this is embedded in the value string
184 * @const char *val@ = value to set, or 0 if the variable must
186 * @unsigned int fl@ = flags to set
187 * @unsigned int force@ = force overwrite of preserved variables
189 * Returns: Pointer to symbol block, or zero if it was deleted.
191 * Use: Puts an item into the environment.
194 static sym_env
*bc__putenv(const char *var
, const char *val
,
195 unsigned int fl
, unsigned int force
)
202 /* --- Sort out embedded variable names --- */
205 const char *p
= strchr(val
, '=');
220 /* --- Find the variable block --- */
223 e
= sym_find(&bc__env
, var
, -1, sizeof(*e
), &f
);
224 if (~e
->f
& envFlag_preserve
|| force
) {
230 e
= sym_find(&bc__env
, var
, -1, 0, 0);
231 if (e
&& (force
|| ~e
->f
& envFlag_preserve
))
232 sym_remove(&bc__env
, e
);
236 /* --- Tidy up and return --- */
245 /* --- @bc__banner@ --- *
247 * Arguments: @FILE *fp@ = stream to write on
251 * Use: Writes a banner containing copyright information.
254 static void bc__banner(FILE *fp
)
256 bc__write(fp
, "$ version " VERSION
"\n");
259 /* --- @bc__usage@ --- *
261 * Arguments: @FILE *fp@ = stream to write on
265 * Use: Writes a terse reminder of command line syntax.
268 static void bc__usage(FILE *fp
)
272 " $ -c <shell-command> <user>\n"
273 " $ [<env-var>] <user> [<command> [<arguments>]...]\n"
274 " $ -d [-p <port>] [-f <config-file>]\n");
277 /* --- @bc__help@ --- *
279 * Arguments: @FILE *fp@ = stream to write on
280 * @int suid@ = whether we're running set-uid
284 * Use: Displays a help message for this excellent piece of software.
287 static void bc__help(FILE *fp
, int suid
)
294 "The `$' program allows you to run a process as another user.\n"
295 "If a command name is given, this is the process executed. If the `-c'\n"
296 "option is used, the process is assumed to be `/bin/sh'. If no command is\n"
297 "given, your default login shell is used.\n"
299 "Your user id, the user id you wish to become, the name of the process\n"
300 "you wish to run, and the identity of the current host are looked up to\n"
301 "ensure that you have permission to do this.\n"
303 "Note that logs are kept of all uses of this program.\n"
305 "Options available are:\n"
307 "-h, --help Display this help text\n"
308 "-u, --usage Display a short usage summary\n"
309 "-v, --version Display $'s version number\n"
311 "-e, --preserve-environment Try to preserve the current environment\n"
312 "-s, --su, --set-user Set environment variables to reflect USER\n"
313 "-l, --login Really log in as USER\n"
315 "-c CMD, --command=CMD Run the (Bourne) shell command CMD\n"
317 "-d, --daemon Start a daemon\n"
318 "-p PORT, --port=PORT In daemon mode, listen on PORT\n"
319 "-f FILE, --config-file=FILE In daemon mode, read config from FILE\n");
324 "-I USER, --impersonate=USER Claim to be USER when asking the server\n");
327 "-T FILE, --trace=FILE Dump trace information to FILE (boring)\n"
328 "-L OPTS, --trace-level=OPTS Set level of tracing information\n");
334 * Arguments: @int argc@ = number of command line arguments
335 * @char *argv[]@ = pointer to the various arguments
337 * Returns: Zero if successful.
339 * Use: Allows a user to change UID.
342 int main(int argc
, char *argv
[])
344 /* --- Request block setup parameters --- */
346 request rq
; /* Request buffer to build */
347 char *cmd
= 0; /* Shell command to execute */
348 char *binary
= "/bin/sh"; /* Default binary to execute */
349 char **env
= environ
; /* Default environment to pass */
350 char **todo
= 0; /* Pointer to argument list */
351 char *who
= 0; /* Who we're meant to become */
352 struct passwd
*from_pw
= 0; /* User we are right now */
353 struct passwd
*to_pw
= 0; /* User we want to become */
355 /* --- Become server setup parameters --- */
357 char *conffile
= file_RULES
; /* Default config file for daemon */
358 int port
= -1; /* Default port for daemon */
360 /* --- Miscellanous shared variables --- */
362 unsigned flags
= 0; /* Various useful flags */
363 int style
= DEFAULT_LOGIN_STYLE
; /* Login style */
365 /* --- Default argument list executes a shell command --- */
367 static char *shell
[] = {
368 "/bin/sh", /* Bourne shell */
369 "-c", /* Read from command line */
370 0, /* Pointer to shell command */
374 /* --- Definitions for the various flags --- */
377 f_daemon
= 1, /* Start up in daemon mode */
378 f_duff
= 2, /* Fault in arguments */
379 f_login
= 4, /* Execute as a login shell */
380 f_dummy
= 8, /* Don't actually do anything */
381 f_setuid
= 16 /* We're running setuid */
384 /* --- Set up the program name --- */
388 if (getuid() != geteuid())
391 /* --- Read the environment into a hashtable --- */
396 sym_createTable(&bc__env
);
397 for (p
= environ
; *p
; p
++)
398 bc__putenv(0, *p
, 0, 0);
401 /* --- Parse some command line arguments --- */
405 static struct option opts
[] = {
407 /* --- Asking for help --- */
409 { "help", 0, 0, 'h' },
410 { "usage", 0, 0, 'u' },
411 { "version", 0, 0, 'v' },
413 /* --- Login style options --- */
415 { "preserve-environment", 0, 0, 'e' },
417 { "set-user", 0, 0, 's' },
418 { "login", 0, 0, 'l' },
420 /* --- Command to run options --- */
422 { "command", gFlag_argReq
, 0, 'c' },
424 /* --- Server options --- */
426 { "daemon", 0, 0, 'd' },
427 { "port", gFlag_argReq
, 0, 'p' },
428 { "config-file", gFlag_argReq
, 0, 'f' },
430 /* --- Tracing options --- */
433 { "impersonate", gFlag_argReq
, 0, 'I' },
434 { "trace", gFlag_argOpt
, 0, 'T' },
435 { "trace-level", gFlag_argOpt
, 0, 'L' },
441 i
= mdwopt(argc
, argv
,
442 "-" "huv" "esl" "c:" "dp:f:" T("I:T::L::"),
443 opts
, 0, 0, gFlag_envVar
);
449 /* --- Asking for help --- */
452 bc__help(stdout
, flags
& f_setuid
);
464 /* --- Login style options --- */
476 /* --- Command to run options --- */
482 /* --- Server options --- */
494 /* --- Pretend to be a different user --- *
496 * There are all sorts of nasty implications for this option. Don't
497 * allow it if we're running setuid. Disable the actual login anyway.
503 if (flags
& f_setuid
)
504 moan("shan't allow impersonation while running setuid");
507 if (isdigit((unsigned char)optarg
[0]))
508 pw
= getpwuid(atoi(optarg
));
510 pw
= getpwnam(optarg
);
512 die("can't impersonate unknown user `%s'", optarg
);
513 from_pw
= userdb_copyUser(pw
);
514 rq
.from
= from_pw
->pw_uid
;
521 /* --- Tracing support --- *
523 * Be careful not to zap a file I wouldn't normally be allowed to write
532 if (optarg
== 0 || strcmp(optarg
, "-") == 0)
535 if ((flags
& f_setuid
) && access(optarg
, W_OK
)) {
536 die("no write permission for trace file file `%s': %s",
537 optarg
, strerror(errno
));
539 if ((fp
= fopen(optarg
, "w")) == 0) {
540 die("couldn't open trace file `%s' for writing: %s",
541 optarg
, strerror(errno
));
544 traceon(fp
, TRACE_DFL
);
545 trace(TRACE_MISC
, "become: tracing enabled");
550 /* --- Setting trace levels --- */
556 unsigned int lvl
= 0, l
;
557 const char *p
= optarg
;
559 /* --- Table of tracing facilities --- */
567 static tr lvltbl
[] = {
568 { 'm', TRACE_MISC
, "miscellaneous messages" },
569 { 's', TRACE_SETUP
, "building the request block" },
570 { 'd', TRACE_DAEMON
, "server process" },
571 { 'r', TRACE_RULE
, "ruleset scanning" },
572 { 'c', TRACE_CHECK
, "request checking" },
573 { 'l', TRACE_CLIENT
, "client process" },
574 { 'R', TRACE_RAND
, "random number generator" },
575 { 'C', TRACE_CRYPTO
, "cryptographic processing of requests" },
576 { 'y', TRACE_YACC
, "parsing configuration file" },
577 { 'D', TRACE_DFL
, "default tracing options" },
578 { 'A', TRACE_ALL
, "all tracing options" },
583 /* --- Output some help if there's no arguemnt --- */
591 for (tp
= lvltbl
; tp
->l
; tp
++) {
592 if ((flags
& f_setuid
) == 0 || tp
->l
& ~TRACE_PRIV
)
593 printf("%c -- %s\n", tp
->ch
, tp
->help
);
597 "Also, `+' and `-' options are recognised to turn on and off vavrious\n"
598 "tracing options. For example, `A-r' enables everything except ruleset\n"
599 "tracing, and `A-D+c' is everything except the defaults, but with request\n"
611 for (tp
= lvltbl
; tp
->l
&& *p
!= tp
->ch
; tp
++)
614 if (flags
& f_setuid
)
617 lvl
= sense ?
(lvl
| l
) : (lvl
& ~l
);
619 moan("unknown trace option `%c'", *p
);
625 yydebug
= ((lvl
& TRACE_YACC
) != 0);
630 /* --- Something that wasn't an option --- *
632 * The following nasties are supported:
634 * * NAME=VALUE -- preserve NAME, and give it a VALUE
635 * * NAME= -- preserve NAME, and give it an empty value
636 * * NAME- -- delete NAME
637 * * NAME! -- preserve NAME with existing value
639 * Anything else is either the user name (which is OK) or the start of
640 * the command (in which case I stop and read the rest of the command).
644 size_t sz
= strcspn(optarg
, "=-!");
647 /* --- None of the above --- */
649 if (optarg
[sz
] == 0 || (optarg
[sz
] != '=' && optarg
[sz
+ 1] != 0)) {
658 /* --- Do the appropriate thing --- */
660 switch (optarg
[sz
]) {
662 bc__putenv(0, optarg
, envFlag_preserve
, 1);
666 bc__putenv(optarg
, 0, 0, 1);
670 if ((e
= sym_find(&bc__env
, optarg
, -1, 0, 0)) != 0)
671 e
->f
|= envFlag_preserve
;
676 /* --- Something I didn't understand has occurred --- */
685 if (flags
& f_duff
) {
690 /* --- Switch to daemon mode if requested --- */
692 if (flags
& f_daemon
) {
693 T( trace(TRACE_MISC
, "become: daemon mode requested"); )
694 daemon_init(conffile
, port
);
698 /* --- Open a syslog --- */
700 openlog(quis(), 0, LOG_AUTH
);
702 /* --- Pick out the uid --- */
712 if (isdigit((unsigned char)who
[0]))
713 pw
= getpwuid(atoi(who
));
717 die("unknown user `%s'", who
);
718 to_pw
= userdb_copyUser(pw
);
722 /* --- Fill in the easy bits of the request --- */
728 pw
= getpwuid(rq
.from
);
730 die("who are you? (can't find user %li)", (long)rq
.from
);
731 from_pw
= userdb_copyUser(pw
);
734 /* --- Find the local host address --- */
740 if ((he
= gethostbyname(u
.nodename
)) == 0)
741 die("who am I? (can't resolve `%s')", u
.nodename
);
742 memcpy(&rq
.host
, he
->h_addr
, sizeof(struct in_addr
));
745 /* --- Shell commands are easy --- */
752 /* --- A command given on the command line isn't too hard --- */
754 else if (optind
< argc
) {
755 todo
= argv
+ optind
;
759 else switch (style
) {
761 /* --- An unadorned becoming requires little work --- */
764 shell
[0] = getenv("SHELL");
766 shell
[0] = from_pw
->pw_shell
;
772 /* --- An su-like login needs slightly less effort --- */
775 shell
[0] = to_pw
->pw_shell
;
781 /* --- A login request needs a little bit of work --- */
784 const char *p
= strrchr(to_pw
->pw_shell
, '/');
790 shell
[0] = xmalloc(strlen(p
) + 2);
792 strcpy(shell
[0] + 1, p
);
795 binary
= to_pw
->pw_shell
;
796 chdir(to_pw
->pw_dir
);
800 /* --- Mangle the environment --- *
802 * This keeps getting more complicated all the time. (How true. Now I've
803 * got all sorts of nasty environment mangling to do.)
805 * The environment stuff now happens in seven phases:
807 * 1. Mark very special variables to be preserved. Currently only TERM
808 * and DISPLAY are treated in this way.
810 * 2. Set and preserve Become's own environment variables.
812 * 3. Set and preserve the user identity variables (USER, LOGNAME, HOME,
813 * SHELL and MAIL) if we're being `su'-like or `login'-like.
815 * 4. If we're preserving the environment or being `su'-like, process the
816 * PATH variable a little. Otherwise reset it to something
819 * 5. If we're being `login'-like, expunge all unpreserved variables.
821 * 6. Expunge any security-critical variables.
823 * 7. Build a new environment table to pass to child processes.
827 /* --- Variables to be preserved always --- *
829 * A user can explicitly expunge a variable in this list, in which case
830 * we never get to see it here.
833 static char *preserve
[] = {
837 /* --- Variables to be expunged --- *
839 * Any environment string which has one of the following as a prefix
840 * will be expunged from the environment passed to the called process.
841 * The first line lists variables which have been used to list search
842 * paths for shared libraries: by manipulating these, an attacker could
843 * replace a standard library with one of his own. The second line lists
844 * other well-known dangerous environment variables.
847 static char *banned
[] = {
848 "-LD_", "SHLIB_PATH", "LIBPATH", "-_RLD_",
849 "IFS", "ENV", "BASH_ENV", "KRB_CONF",
853 /* --- Other useful variables --- */
862 /* --- Stage one. Preserve display-specific variables --- */
864 for (pp
= preserve
; *pp
; pp
++) {
865 if ((e
= sym_find(&bc__env
, *pp
, -1, 0, 0)) != 0)
866 e
->f
|= envFlag_preserve
;
869 /* --- Stage two. Set Become's own variables --- */
871 e
= sym_find(&bc__env
, "BECOME_ORIGINAL_USER", -1, sizeof(*e
), &f
);
873 bc__setenv(e
, from_pw
->pw_name
);
874 e
->f
|= envFlag_preserve
;
876 e
= sym_find(&bc__env
, "BECOME_ORIGINAL_HOME", -1, sizeof(*e
), &f
);
878 bc__setenv(e
, from_pw
->pw_dir
);
879 e
->f
|= envFlag_preserve
;
881 bc__putenv("BECOME_OLD_USER", from_pw
->pw_name
, envFlag_preserve
, 0);
882 bc__putenv("BECOME_OLD_HOME", from_pw
->pw_dir
, envFlag_preserve
, 0);
883 bc__putenv("BECOME_USER", to_pw
->pw_name
, envFlag_preserve
, 0);
884 bc__putenv("BECOME_HOME", to_pw
->pw_dir
, envFlag_preserve
, 0);
886 /* --- Stage three. Set user identity --- */
890 static char *maildirs
[] = {
891 "/var/spool/mail", "/var/mail",
892 "/usr/spool/mail", "/usr/mail",
898 for (pp
= maildirs
; *pp
; pp
++) {
899 if (stat(*pp
, &s
) == 0 && S_ISDIR(s
.st_mode
)) {
900 sprintf(b
, "%s/%s", *pp
, to_pw
->pw_name
);
901 bc__putenv("MAIL", b
, envFlag_preserve
, 0);
908 bc__putenv("USER", to_pw
->pw_name
, envFlag_preserve
, 0);
909 bc__putenv("LOGNAME", to_pw
->pw_name
, envFlag_preserve
, 0);
910 bc__putenv("HOME", to_pw
->pw_dir
, envFlag_preserve
, 0);
911 bc__putenv("SHELL", to_pw
->pw_shell
, envFlag_preserve
, 0);
915 /* --- Stage four. Set the user's PATH properly --- */
918 /* --- Find an existing path --- *
920 * If there's no path, or this is a login, then set a default path,
921 * unless we're meant to preserve the existing one. Whew!
924 e
= sym_find(&bc__env
, "PATH", -1, sizeof(*e
), &f
);
926 if (!f
|| (style
== l_login
&& ~e
->f
& envFlag_preserve
)) {
928 rq
.to ?
"/usr/bin:/bin" : "/usr/bin:/usr/sbin:/bin:/sbin",
929 envFlag_preserve
, 0);
932 /* --- Find the string --- */
934 e
->f
= envFlag_preserve
;
935 p
= strchr(e
->val
, '=') + 1;
938 /* --- Write the new version to a dynamically allocated buffer --- */
940 e
->val
= xmalloc(4 + 1 + strlen(p
) + 1);
941 strcpy(e
->val
, "PATH=");
944 for (p
= strtok(p
, ":"); p
; p
= strtok(0, ":")) {
959 /* --- Stages five and six. Expunge variables and count numbers --- *
961 * Folded together, so I only need one pass through the table. Also
962 * count the number of variables needed at this time.
967 for (sym_createIter(&i
, &bc__env
); (e
= sym_next(&i
)) != 0; ) {
969 /* --- Login style expunges all unpreserved variables --- */
971 if (style
== l_login
&& ~e
->f
& envFlag_preserve
)
974 /* --- Otherwise just check the name against the list --- */
976 for (pp
= banned
; *pp
; pp
++) {
979 if (memcmp(e
->_base
.name
, p
, strlen(p
)) == 0)
981 } else if (strcmp(e
->_base
.name
, p
) == 0)
989 sym_remove(&bc__env
, e
);
992 /* --- Stage seven. Build the new environment block --- */
994 env
= qq
= xmalloc((sz
+ 1) * sizeof(*qq
));
996 for (sym_createIter(&i
, &bc__env
); (e
= sym_next(&i
)) != 0; )
1001 /* --- Trace the command --- */
1003 IF_TRACING(TRACE_SETUP
, {
1006 trace(TRACE_SETUP
, "setup: from user %s to user %s",
1007 from_pw
->pw_name
, to_pw
->pw_name
);
1008 trace(TRACE_SETUP
, "setup: binary == `%s'", binary
);
1009 for (i
= 0; todo
[i
]; i
++)
1010 trace(TRACE_SETUP
, "setup: arg %i == `%s'", i
, todo
[i
]);
1011 for (i
= 0; env
[i
]; i
++)
1012 trace(TRACE_SETUP
, "setup: env %i == `%s'", i
, env
[i
]);
1015 /* --- If necessary, resolve the path to the command --- */
1017 if (!strchr(binary
, '/')) {
1021 if ((p
= getenv("PATH")) == 0)
1022 p
= "/bin:/usr/bin";
1025 for (p
= strtok(path
, ":"); p
; p
= strtok(0, ":")) {
1027 /* --- Check length of string before copying --- */
1029 if (strlen(p
) + strlen(binary
) + 2 > sizeof(rq
.cmd
))
1032 /* --- Now build the pathname and check it --- */
1034 sprintf(rq
.cmd
, "%s/%s", p
, todo
[0]);
1035 if (stat(rq
.cmd
, &st
) == 0 && /* Check it exists */
1036 st
.st_mode
& 0111 && /* Check it's executable */
1037 (st
.st_mode
& S_IFMT
) == S_IFREG
) /* Check it's a file */
1042 die("couldn't find `%s' in path", todo
[0]);
1046 T( trace(TRACE_SETUP
, "setup: resolve binary to `%s'", binary
); )
1048 /* --- Canonicalise the path string, if necessary --- */
1055 /* --- Insert current directory name if path not absolute --- */
1060 if (!getcwd(b
, sizeof(b
)))
1061 die("couldn't read current directory: %s", strerror(errno
));
1066 /* --- Now copy over characters from the path string --- */
1070 /* --- Check for buffer overflows here --- *
1072 * I write at most one byte per iteration so this is OK. Remember to
1073 * allow one for the null byte.
1076 if (p
>= b
+ sizeof(b
) - 1)
1077 die("internal error: buffer overflow while canonifying path");
1079 /* --- Reduce multiple slashes to just one --- */
1087 /* --- Handle dots in filenames --- *
1089 * @p[-1]@ is valid here, because if @*q@ is not a `/' then either
1090 * we've just stuck the current directory on the end of the buffer,
1091 * or we've just put something else on the end.
1094 else if (*q
== '.' && p
[-1] == '/') {
1096 /* --- A simple `./' just gets removed --- */
1098 if (q
[1] == 0 || q
[1] == '/') {
1104 /* --- A `../' needs to be peeled back to the previous `/' --- */
1106 if (q
[1] == '.' && (q
[2] == 0 || q
[2] == '/')) {
1109 while (p
> b
&& p
[-1] != '/')
1122 T( trace(TRACE_SETUP
, "setup: canonify binary to `%s'", rq
.cmd
); )
1124 /* --- Run the check --- *
1126 * If the user is already what she wants to be, then print a warning.
1127 * Then, if I was just going to spawn a shell, quit, to reduce user
1128 * confusion. Otherwise, do what was wanted anyway.
1131 if (rq
.from
== rq
.to
) {
1132 moan("you already are `%s'!", to_pw
->pw_name
);
1133 if (!cmd
&& todo
== shell
) {
1134 moan("(to prevent confusion, I'm not spawning a shell)");
1141 "permission %s for %s to become %s to run `%s'",
1142 a ?
"granted" : "denied", from_pw
->pw_name
, to_pw
->pw_name
,
1146 die("permission denied");
1149 /* --- Now do the job --- */
1151 T( trace(TRACE_MISC
, "become: permission granted"); )
1153 if (flags
& f_dummy
) {
1154 puts("permission granted");
1157 if (setuid(rq
.to
) == -1)
1158 die("couldn't set uid: %s", strerror(errno
));
1159 execve(rq
.cmd
, todo
, env
);
1160 die("couldn't exec `%s': %s", rq
.cmd
, strerror(errno
));
1165 /*----- That's all, folks -------------------------------------------------*/