3 * $Id: become.c,v 1.8 1997/09/08 13:56:24 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.8 1997/09/08 13:56:24 mdw
33 * Change criteria for expunging items from the user's PATH: instead of
34 * removing things starting with `.', remove things not starting with `/'.
36 * Revision 1.7 1997/09/08 13:43:20 mdw
37 * Change userid when creating tracefiles rather than fiddling with
38 * `access': it works rather better. Also, insert some stdio buffer
39 * flushing to ensure tracedumps are completely written.
41 * Revision 1.6 1997/09/05 13:47:44 mdw
42 * Make the `-L' (trace-level) option's argument optional, like the long
45 * Revision 1.5 1997/09/05 11:45:19 mdw
46 * Add support for different login styles, and environment variable
47 * manipulation in a safe and useful way.
49 * Revision 1.4 1997/08/20 16:15:13 mdw
50 * Overhaul of environment handling. Fix daft bug in path search code.
52 * Revision 1.3 1997/08/07 16:28:59 mdw
53 * Do something useful when users attempt to become themselves.
55 * Revision 1.2 1997/08/04 10:24:20 mdw
56 * Sources placed under CVS control.
58 * Revision 1.1 1997/07/21 13:47:54 mdw
63 /*----- Header files ------------------------------------------------------*/
65 /* --- ANSI headers --- */
74 /* --- Unix headers --- */
76 #include <sys/types.h>
78 #include <sys/socket.h>
79 #include <sys/utsname.h>
81 #include <netinet/in.h>
83 #include <arpa/inet.h>
90 extern char **environ
;
92 /* --- Local headers --- */
107 /*----- Type definitions --------------------------------------------------*/
109 /* --- Symbol table entry for an environment variable --- */
111 typedef struct sym_env
{
112 sym_base _base
; /* Symbol table information */
113 unsigned f
; /* Flags word (see below) */
114 char *val
; /* Pointer to variable value */
117 /* --- Environment variable flags --- */
123 /* --- Login behaviour types --- */
126 l_preserve
, /* Preserve the environment */
127 l_user
, /* Update who I am */
128 l_login
/* Do a full login */
131 /*----- Static variables --------------------------------------------------*/
133 static sym_table bc__env
;
135 /*----- Main code ---------------------------------------------------------*/
137 /* --- @bc__write@ --- *
139 * Arguments: @FILE *fp@ = pointer to a stream to write on
140 * @const char *p@ = pointer to a string
144 * Use: Writes the string to the stream, substituting the program
145 * name (as returned by @quis@) for each occurrence of the
149 static void bc__write(FILE *fp
, const char *p
)
151 const char *n
= quis();
153 size_t nl
= strlen(n
);
155 /* --- Try to be a little efficient --- *
157 * Gather up non-`$' characters using @strcspn@ and spew them out really
168 fwrite(n
, nl
, 1, fp
);
173 /* --- @bc__setenv@ --- *
175 * Arguments: @sym_env *e@ = pointer to environment variable block
176 * @const char *val@ = value to set
180 * Use: Sets an environment variable block to the right value.
183 static void bc__setenv(sym_env
*e
, const char *val
)
185 e
->val
= xmalloc(strlen(e
->_base
.name
) + 1 + strlen(val
) + 1);
186 sprintf(e
->val
, "%s=%s", e
->_base
.name
, val
);
189 /* --- @bc__putenv@ --- *
191 * Arguments: @const char *var@ = name of the variable to set, or 0 if
192 * this is embedded in the value string
193 * @const char *val@ = value to set, or 0 if the variable must
195 * @unsigned int fl@ = flags to set
196 * @unsigned int force@ = force overwrite of preserved variables
198 * Returns: Pointer to symbol block, or zero if it was deleted.
200 * Use: Puts an item into the environment.
203 static sym_env
*bc__putenv(const char *var
, const char *val
,
204 unsigned int fl
, unsigned int force
)
211 /* --- Sort out embedded variable names --- */
214 const char *p
= strchr(val
, '=');
229 /* --- Find the variable block --- */
232 e
= sym_find(&bc__env
, var
, -1, sizeof(*e
), &f
);
233 if (~e
->f
& envFlag_preserve
|| force
) {
239 e
= sym_find(&bc__env
, var
, -1, 0, 0);
240 if (e
&& (force
|| ~e
->f
& envFlag_preserve
))
241 sym_remove(&bc__env
, e
);
245 /* --- Tidy up and return --- */
254 /* --- @bc__banner@ --- *
256 * Arguments: @FILE *fp@ = stream to write on
260 * Use: Writes a banner containing copyright information.
263 static void bc__banner(FILE *fp
)
265 bc__write(fp
, "$ version " VERSION
"\n");
268 /* --- @bc__usage@ --- *
270 * Arguments: @FILE *fp@ = stream to write on
274 * Use: Writes a terse reminder of command line syntax.
277 static void bc__usage(FILE *fp
)
281 " $ -c <shell-command> <user>\n"
282 " $ [<env-var>] <user> [<command> [<arguments>]...]\n"
283 " $ -d [-p <port>] [-f <config-file>]\n");
286 /* --- @bc__help@ --- *
288 * Arguments: @FILE *fp@ = stream to write on
289 * @int suid@ = whether we're running set-uid
293 * Use: Displays a help message for this excellent piece of software.
296 static void bc__help(FILE *fp
, int suid
)
303 "The `$' program allows you to run a process as another user.\n"
304 "If a command name is given, this is the process executed. If the `-c'\n"
305 "option is used, the process is assumed to be `/bin/sh'. If no command is\n"
306 "given, your default login shell is used.\n"
308 "Your user id, the user id you wish to become, the name of the process\n"
309 "you wish to run, and the identity of the current host are looked up to\n"
310 "ensure that you have permission to do this.\n"
312 "Note that logs are kept of all uses of this program.\n"
314 "Options available are:\n"
316 "-h, --help Display this help text\n"
317 "-u, --usage Display a short usage summary\n"
318 "-v, --version Display $'s version number\n"
320 "-e, --preserve-environment Try to preserve the current environment\n"
321 "-s, --su, --set-user Set environment variables to reflect USER\n"
322 "-l, --login Really log in as USER\n"
324 "-c CMD, --command=CMD Run the (Bourne) shell command CMD\n"
326 "-d, --daemon Start a daemon\n"
327 "-p PORT, --port=PORT In daemon mode, listen on PORT\n"
328 "-f FILE, --config-file=FILE In daemon mode, read config from FILE\n");
333 "-I USER, --impersonate=USER Claim to be USER when asking the server\n");
336 "-T FILE, --trace=FILE Dump trace information to FILE (boring)\n"
337 "-L OPTS, --trace-level=OPTS Set level of tracing information\n");
343 * Arguments: @int argc@ = number of command line arguments
344 * @char *argv[]@ = pointer to the various arguments
346 * Returns: Zero if successful.
348 * Use: Allows a user to change UID.
351 int main(int argc
, char *argv
[])
353 /* --- Request block setup parameters --- */
355 request rq
; /* Request buffer to build */
356 char *cmd
= 0; /* Shell command to execute */
357 char *binary
= "/bin/sh"; /* Default binary to execute */
358 char **env
= environ
; /* Default environment to pass */
359 char **todo
= 0; /* Pointer to argument list */
360 char *who
= 0; /* Who we're meant to become */
361 struct passwd
*from_pw
= 0; /* User we are right now */
362 struct passwd
*to_pw
= 0; /* User we want to become */
364 /* --- Become server setup parameters --- */
366 char *conffile
= file_RULES
; /* Default config file for daemon */
367 int port
= -1; /* Default port for daemon */
369 /* --- Miscellanous shared variables --- */
371 unsigned flags
= 0; /* Various useful flags */
372 int style
= DEFAULT_LOGIN_STYLE
; /* Login style */
374 /* --- Default argument list executes a shell command --- */
376 static char *shell
[] = {
377 "/bin/sh", /* Bourne shell */
378 "-c", /* Read from command line */
379 0, /* Pointer to shell command */
383 /* --- Definitions for the various flags --- */
386 f_daemon
= 1, /* Start up in daemon mode */
387 f_duff
= 2, /* Fault in arguments */
388 f_login
= 4, /* Execute as a login shell */
389 f_dummy
= 8, /* Don't actually do anything */
390 f_setuid
= 16 /* We're running setuid */
393 /* --- Set up the program name --- */
397 if (getuid() != geteuid())
400 /* --- Read the environment into a hashtable --- */
405 sym_createTable(&bc__env
);
406 for (p
= environ
; *p
; p
++)
407 bc__putenv(0, *p
, 0, 0);
410 /* --- Parse some command line arguments --- */
414 static struct option opts
[] = {
416 /* --- Asking for help --- */
418 { "help", 0, 0, 'h' },
419 { "usage", 0, 0, 'u' },
420 { "version", 0, 0, 'v' },
422 /* --- Login style options --- */
424 { "preserve-environment", 0, 0, 'e' },
426 { "set-user", 0, 0, 's' },
427 { "login", 0, 0, 'l' },
429 /* --- Command to run options --- */
431 { "command", gFlag_argReq
, 0, 'c' },
433 /* --- Server options --- */
435 { "daemon", 0, 0, 'd' },
436 { "port", gFlag_argReq
, 0, 'p' },
437 { "config-file", gFlag_argReq
, 0, 'f' },
439 /* --- Tracing options --- */
442 { "impersonate", gFlag_argReq
, 0, 'I' },
443 { "trace", gFlag_argOpt
, 0, 'T' },
444 { "trace-level", gFlag_argOpt
, 0, 'L' },
450 i
= mdwopt(argc
, argv
,
451 "-" "huv" "esl" "c:" "dp:f:" T("I:T::L::"),
452 opts
, 0, 0, gFlag_envVar
);
458 /* --- Asking for help --- */
461 bc__help(stdout
, flags
& f_setuid
);
473 /* --- Login style options --- */
485 /* --- Command to run options --- */
491 /* --- Server options --- */
503 /* --- Pretend to be a different user --- *
505 * There are all sorts of nasty implications for this option. Don't
506 * allow it if we're running setuid. Disable the actual login anyway.
512 if (flags
& f_setuid
)
513 moan("shan't allow impersonation while running setuid");
516 if (isdigit((unsigned char)optarg
[0]))
517 pw
= getpwuid(atoi(optarg
));
519 pw
= getpwnam(optarg
);
521 die("can't impersonate unknown user `%s'", optarg
);
522 from_pw
= userdb_copyUser(pw
);
523 rq
.from
= from_pw
->pw_uid
;
530 /* --- Tracing support --- *
532 * Be careful not to zap a file I wouldn't normally be allowed to write
541 if (optarg
== 0 || strcmp(optarg
, "-") == 0)
544 uid_t eu
= geteuid(), ru
= getuid();
547 if (setreuid(eu
, ru
))
552 die("couldn't temporarily give up privileges: %s",
556 if ((fp
= fopen(optarg
, "w")) == 0) {
557 die("couldn't open trace file `%s' for writing: %s",
558 optarg
, strerror(errno
));
562 if (setreuid(ru
, eu
))
566 die("couldn't regain privileges: %s", strerror(errno
));
568 traceon(fp
, TRACE_DFL
);
569 trace(TRACE_MISC
, "become: tracing enabled");
574 /* --- Setting trace levels --- */
580 unsigned int lvl
= 0, l
;
581 const char *p
= optarg
;
583 /* --- Table of tracing facilities --- */
591 static tr lvltbl
[] = {
592 { 'm', TRACE_MISC
, "miscellaneous messages" },
593 { 's', TRACE_SETUP
, "building the request block" },
594 { 'd', TRACE_DAEMON
, "server process" },
595 { 'r', TRACE_RULE
, "ruleset scanning" },
596 { 'c', TRACE_CHECK
, "request checking" },
597 { 'l', TRACE_CLIENT
, "client process" },
598 { 'R', TRACE_RAND
, "random number generator" },
599 { 'C', TRACE_CRYPTO
, "cryptographic processing of requests" },
600 { 'y', TRACE_YACC
, "parsing configuration file" },
601 { 'D', TRACE_DFL
, "default tracing options" },
602 { 'A', TRACE_ALL
, "all tracing options" },
607 /* --- Output some help if there's no arguemnt --- */
615 for (tp
= lvltbl
; tp
->l
; tp
++) {
616 if ((flags
& f_setuid
) == 0 || tp
->l
& ~TRACE_PRIV
)
617 printf("%c -- %s\n", tp
->ch
, tp
->help
);
621 "Also, `+' and `-' options are recognised to turn on and off vavrious\n"
622 "tracing options. For example, `A-r' enables everything except ruleset\n"
623 "tracing, and `A-D+c' is everything except the defaults, but with request\n"
635 for (tp
= lvltbl
; tp
->l
&& *p
!= tp
->ch
; tp
++)
638 if (flags
& f_setuid
)
641 lvl
= sense ?
(lvl
| l
) : (lvl
& ~l
);
643 moan("unknown trace option `%c'", *p
);
649 yydebug
= ((lvl
& TRACE_YACC
) != 0);
654 /* --- Something that wasn't an option --- *
656 * The following nasties are supported:
658 * * NAME=VALUE -- preserve NAME, and give it a VALUE
659 * * NAME= -- preserve NAME, and give it an empty value
660 * * NAME- -- delete NAME
661 * * NAME! -- preserve NAME with existing value
663 * Anything else is either the user name (which is OK) or the start of
664 * the command (in which case I stop and read the rest of the command).
668 size_t sz
= strcspn(optarg
, "=-!");
671 /* --- None of the above --- */
673 if (optarg
[sz
] == 0 || (optarg
[sz
] != '=' && optarg
[sz
+ 1] != 0)) {
682 /* --- Do the appropriate thing --- */
684 switch (optarg
[sz
]) {
686 bc__putenv(0, optarg
, envFlag_preserve
, 1);
690 bc__putenv(optarg
, 0, 0, 1);
694 if ((e
= sym_find(&bc__env
, optarg
, -1, 0, 0)) != 0)
695 e
->f
|= envFlag_preserve
;
700 /* --- Something I didn't understand has occurred --- */
709 if (flags
& f_duff
) {
714 /* --- Switch to daemon mode if requested --- */
716 if (flags
& f_daemon
) {
717 T( trace(TRACE_MISC
, "become: daemon mode requested"); )
718 daemon_init(conffile
, port
);
722 /* --- Open a syslog --- */
724 openlog(quis(), 0, LOG_AUTH
);
726 /* --- Pick out the uid --- */
736 if (isdigit((unsigned char)who
[0]))
737 pw
= getpwuid(atoi(who
));
741 die("unknown user `%s'", who
);
742 to_pw
= userdb_copyUser(pw
);
746 /* --- Fill in the easy bits of the request --- */
752 pw
= getpwuid(rq
.from
);
754 die("who are you? (can't find user %li)", (long)rq
.from
);
755 from_pw
= userdb_copyUser(pw
);
758 /* --- Find the local host address --- */
764 if ((he
= gethostbyname(u
.nodename
)) == 0)
765 die("who am I? (can't resolve `%s')", u
.nodename
);
766 memcpy(&rq
.host
, he
->h_addr
, sizeof(struct in_addr
));
769 /* --- Shell commands are easy --- */
776 /* --- A command given on the command line isn't too hard --- */
778 else if (optind
< argc
) {
779 todo
= argv
+ optind
;
783 else switch (style
) {
785 /* --- An unadorned becoming requires little work --- */
788 shell
[0] = getenv("SHELL");
790 shell
[0] = from_pw
->pw_shell
;
796 /* --- An su-like login needs slightly less effort --- */
799 shell
[0] = to_pw
->pw_shell
;
805 /* --- A login request needs a little bit of work --- */
808 const char *p
= strrchr(to_pw
->pw_shell
, '/');
814 shell
[0] = xmalloc(strlen(p
) + 2);
816 strcpy(shell
[0] + 1, p
);
819 binary
= to_pw
->pw_shell
;
820 chdir(to_pw
->pw_dir
);
824 /* --- Mangle the environment --- *
826 * This keeps getting more complicated all the time. (How true. Now I've
827 * got all sorts of nasty environment mangling to do.)
829 * The environment stuff now happens in seven phases:
831 * 1. Mark very special variables to be preserved. Currently only TERM
832 * and DISPLAY are treated in this way.
834 * 2. Set and preserve Become's own environment variables.
836 * 3. Set and preserve the user identity variables (USER, LOGNAME, HOME,
837 * SHELL and MAIL) if we're being `su'-like or `login'-like.
839 * 4. If we're preserving the environment or being `su'-like, process the
840 * PATH variable a little. Otherwise reset it to something
843 * 5. If we're being `login'-like, expunge all unpreserved variables.
845 * 6. Expunge any security-critical variables.
847 * 7. Build a new environment table to pass to child processes.
851 /* --- Variables to be preserved always --- *
853 * A user can explicitly expunge a variable in this list, in which case
854 * we never get to see it here.
857 static char *preserve
[] = {
861 /* --- Variables to be expunged --- *
863 * Any environment string which has one of the following as a prefix will
864 * be expunged from the environment passed to the called process. The
865 * first line lists variables which have been used to list search paths
866 * for shared libraries: by manipulating these, an attacker could replace
867 * a standard library with one of his own. The second line lists other
868 * well-known dangerous environment variables.
871 static char *banned
[] = {
872 "-LD_", "SHLIB_PATH", "LIBPATH", "-_RLD_",
873 "IFS", "ENV", "BASH_ENV", "KRB_CONF",
877 /* --- Other useful variables --- */
886 /* --- Stage one. Preserve display-specific variables --- */
888 for (pp
= preserve
; *pp
; pp
++) {
889 if ((e
= sym_find(&bc__env
, *pp
, -1, 0, 0)) != 0)
890 e
->f
|= envFlag_preserve
;
893 /* --- Stage two. Set Become's own variables --- */
895 e
= sym_find(&bc__env
, "BECOME_ORIGINAL_USER", -1, sizeof(*e
), &f
);
897 bc__setenv(e
, from_pw
->pw_name
);
898 e
->f
|= envFlag_preserve
;
900 e
= sym_find(&bc__env
, "BECOME_ORIGINAL_HOME", -1, sizeof(*e
), &f
);
902 bc__setenv(e
, from_pw
->pw_dir
);
903 e
->f
|= envFlag_preserve
;
905 bc__putenv("BECOME_OLD_USER", from_pw
->pw_name
, envFlag_preserve
, 0);
906 bc__putenv("BECOME_OLD_HOME", from_pw
->pw_dir
, envFlag_preserve
, 0);
907 bc__putenv("BECOME_USER", to_pw
->pw_name
, envFlag_preserve
, 0);
908 bc__putenv("BECOME_HOME", to_pw
->pw_dir
, envFlag_preserve
, 0);
910 /* --- Stage three. Set user identity --- */
914 static char *maildirs
[] = {
915 "/var/spool/mail", "/var/mail",
916 "/usr/spool/mail", "/usr/mail",
922 for (pp
= maildirs
; *pp
; pp
++) {
923 if (stat(*pp
, &s
) == 0 && S_ISDIR(s
.st_mode
)) {
924 sprintf(b
, "%s/%s", *pp
, to_pw
->pw_name
);
925 bc__putenv("MAIL", b
, envFlag_preserve
, 0);
932 bc__putenv("USER", to_pw
->pw_name
, envFlag_preserve
, 0);
933 bc__putenv("LOGNAME", to_pw
->pw_name
, envFlag_preserve
, 0);
934 bc__putenv("HOME", to_pw
->pw_dir
, envFlag_preserve
, 0);
935 bc__putenv("SHELL", to_pw
->pw_shell
, envFlag_preserve
, 0);
939 /* --- Stage four. Set the user's PATH properly --- */
942 /* --- Find an existing path --- *
944 * If there's no path, or this is a login, then set a default path,
945 * unless we're meant to preserve the existing one. Whew!
948 e
= sym_find(&bc__env
, "PATH", -1, sizeof(*e
), &f
);
950 if (!f
|| (style
== l_login
&& ~e
->f
& envFlag_preserve
)) {
952 rq
.to ?
"/usr/bin:/bin" : "/usr/bin:/usr/sbin:/bin:/sbin",
953 envFlag_preserve
, 0);
956 /* --- Find the string --- */
958 e
->f
= envFlag_preserve
;
959 p
= strchr(e
->val
, '=') + 1;
962 /* --- Write the new version to a dynamically allocated buffer --- */
964 e
->val
= xmalloc(4 + 1 + strlen(p
) + 1);
965 strcpy(e
->val
, "PATH=");
968 for (p
= strtok(p
, ":"); p
; p
= strtok(0, ":")) {
983 /* --- Stages five and six. Expunge variables and count numbers --- *
985 * Folded together, so I only need one pass through the table. Also
986 * count the number of variables needed at this time.
991 for (sym_createIter(&i
, &bc__env
); (e
= sym_next(&i
)) != 0; ) {
993 /* --- Login style expunges all unpreserved variables --- */
995 if (style
== l_login
&& ~e
->f
& envFlag_preserve
)
998 /* --- Otherwise just check the name against the list --- */
1000 for (pp
= banned
; *pp
; pp
++) {
1003 if (memcmp(e
->_base
.name
, p
, strlen(p
)) == 0)
1005 } else if (strcmp(e
->_base
.name
, p
) == 0)
1013 sym_remove(&bc__env
, e
);
1016 /* --- Stage seven. Build the new environment block --- */
1018 env
= qq
= xmalloc((sz
+ 1) * sizeof(*qq
));
1020 for (sym_createIter(&i
, &bc__env
); (e
= sym_next(&i
)) != 0; )
1025 /* --- Trace the command --- */
1027 IF_TRACING(TRACE_SETUP
, {
1030 trace(TRACE_SETUP
, "setup: from user %s to user %s",
1031 from_pw
->pw_name
, to_pw
->pw_name
);
1032 trace(TRACE_SETUP
, "setup: binary == `%s'", binary
);
1033 for (i
= 0; todo
[i
]; i
++)
1034 trace(TRACE_SETUP
, "setup: arg %i == `%s'", i
, todo
[i
]);
1035 for (i
= 0; env
[i
]; i
++)
1036 trace(TRACE_SETUP
, "setup: env %i == `%s'", i
, env
[i
]);
1039 /* --- If necessary, resolve the path to the command --- */
1041 if (!strchr(binary
, '/')) {
1045 if ((p
= getenv("PATH")) == 0)
1046 p
= "/bin:/usr/bin";
1049 for (p
= strtok(path
, ":"); p
; p
= strtok(0, ":")) {
1051 /* --- Check length of string before copying --- */
1053 if (strlen(p
) + strlen(binary
) + 2 > sizeof(rq
.cmd
))
1056 /* --- Now build the pathname and check it --- */
1058 sprintf(rq
.cmd
, "%s/%s", p
, todo
[0]);
1059 if (stat(rq
.cmd
, &st
) == 0 && /* Check it exists */
1060 st
.st_mode
& 0111 && /* Check it's executable */
1061 S_ISREG(st
.st_mode
)) /* Check it's a file */
1066 die("couldn't find `%s' in path", todo
[0]);
1070 T( trace(TRACE_SETUP
, "setup: resolve binary to `%s'", binary
); )
1072 /* --- Canonicalise the path string, if necessary --- */
1079 /* --- Insert current directory name if path not absolute --- */
1084 if (!getcwd(b
, sizeof(b
)))
1085 die("couldn't read current directory: %s", strerror(errno
));
1090 /* --- Now copy over characters from the path string --- */
1094 /* --- Check for buffer overflows here --- *
1096 * I write at most one byte per iteration so this is OK. Remember to
1097 * allow one for the null byte.
1100 if (p
>= b
+ sizeof(b
) - 1)
1101 die("internal error: buffer overflow while canonifying path");
1103 /* --- Reduce multiple slashes to just one --- */
1111 /* --- Handle dots in filenames --- *
1113 * @p[-1]@ is valid here, because if @*q@ is not a `/' then either
1114 * we've just stuck the current directory on the end of the buffer,
1115 * or we've just put something else on the end.
1118 else if (*q
== '.' && p
[-1] == '/') {
1120 /* --- A simple `./' just gets removed --- */
1122 if (q
[1] == 0 || q
[1] == '/') {
1128 /* --- A `../' needs to be peeled back to the previous `/' --- */
1130 if (q
[1] == '.' && (q
[2] == 0 || q
[2] == '/')) {
1133 while (p
> b
&& p
[-1] != '/')
1146 T( trace(TRACE_SETUP
, "setup: canonify binary to `%s'", rq
.cmd
); )
1148 /* --- Run the check --- *
1150 * If the user is already what she wants to be, then print a warning.
1151 * Then, if I was just going to spawn a shell, quit, to reduce user
1152 * confusion. Otherwise, do what was wanted anyway.
1155 if (rq
.from
== rq
.to
) {
1156 moan("you already are `%s'!", to_pw
->pw_name
);
1157 if (!cmd
&& todo
== shell
) {
1158 moan("(to prevent confusion, I'm not spawning a shell)");
1165 "permission %s for %s to become %s to run `%s'",
1166 a ?
"granted" : "denied", from_pw
->pw_name
, to_pw
->pw_name
,
1170 die("permission denied");
1173 /* --- Now do the job --- */
1175 T( trace(TRACE_MISC
, "become: permission granted"); )
1177 if (flags
& f_dummy
) {
1178 puts("permission granted");
1181 if (setuid(rq
.to
) == -1)
1182 die("couldn't set uid: %s", strerror(errno
));
1184 execve(rq
.cmd
, todo
, env
);
1185 die("couldn't exec `%s': %s", rq
.cmd
, strerror(errno
));
1190 /*----- That's all, folks -------------------------------------------------*/