e73f5dac0badf94c0ee748052d40fb336991c71e
3 * $Id: become.c,v 1.11 1997/09/24 09:48:45 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.11 1997/09/24 09:48:45 mdw
33 * Fix (scary) overrun bug in group allocation stuff.
35 * Revision 1.10 1997/09/17 10:14:10 mdw
36 * Fix a typo. Support service names in `--port' option.
38 * Revision 1.9 1997/09/10 10:28:05 mdw
39 * Allow default port to be given as a service name or port number. Handle
40 * groups properly (lots of options here).
42 * Revision 1.8 1997/09/08 13:56:24 mdw
43 * Change criteria for expunging items from the user's PATH: instead of
44 * removing things starting with `.', remove things not starting with `/'.
46 * Revision 1.7 1997/09/08 13:43:20 mdw
47 * Change userid when creating tracefiles rather than fiddling with
48 * `access': it works rather better. Also, insert some stdio buffer
49 * flushing to ensure tracedumps are completely written.
51 * Revision 1.6 1997/09/05 13:47:44 mdw
52 * Make the `-L' (trace-level) option's argument optional, like the long
55 * Revision 1.5 1997/09/05 11:45:19 mdw
56 * Add support for different login styles, and environment variable
57 * manipulation in a safe and useful way.
59 * Revision 1.4 1997/08/20 16:15:13 mdw
60 * Overhaul of environment handling. Fix daft bug in path search code.
62 * Revision 1.3 1997/08/07 16:28:59 mdw
63 * Do something useful when users attempt to become themselves.
65 * Revision 1.2 1997/08/04 10:24:20 mdw
66 * Sources placed under CVS control.
68 * Revision 1.1 1997/07/21 13:47:54 mdw
73 /*----- Header files ------------------------------------------------------*/
75 /* --- ANSI headers --- */
85 /* --- Unix headers --- */
87 #include <sys/types.h>
89 #include <sys/socket.h>
90 #include <sys/utsname.h>
92 #include <netinet/in.h>
94 #include <arpa/inet.h>
102 extern char **environ
;
104 /* --- Local headers --- */
119 /*----- Type definitions --------------------------------------------------*/
121 /* --- Symbol table entry for an environment variable --- */
123 typedef struct sym_env
{
124 sym_base _base
; /* Symbol table information */
125 unsigned f
; /* Flags word (see below) */
126 char *val
; /* Pointer to variable value */
129 /* --- Environment variable flags --- */
135 /* --- Login behaviour types --- */
138 l_preserve
, /* Preserve the environment */
139 l_setuser
, /* Update who I am */
140 l_login
/* Do a full login */
143 /* --- Group behaviour types --- *
145 * Note that these make a handy bitfield.
148 #ifdef HAVE_SETGROUPS
151 g_unset
= 0, /* Nobody's set a preference */
152 g_keep
= 1, /* Leave the group memberships */
153 g_replace
= 2, /* Replace group memberships */
154 g_merge
= (g_keep
| g_replace
) /* Merge the group memberships */
159 /*----- Static variables --------------------------------------------------*/
161 static sym_table bc__env
;
163 /*----- Main code ---------------------------------------------------------*/
165 /* --- @bc__write@ --- *
167 * Arguments: @FILE *fp@ = pointer to a stream to write on
168 * @const char *p@ = pointer to a string
172 * Use: Writes the string to the stream, substituting the program
173 * name (as returned by @quis@) for each occurrence of the
177 static void bc__write(FILE *fp
, const char *p
)
179 const char *n
= quis();
181 size_t nl
= strlen(n
);
183 /* --- Try to be a little efficient --- *
185 * Gather up non-`$' characters using @strcspn@ and spew them out really
196 fwrite(n
, nl
, 1, fp
);
201 /* --- @bc__setenv@ --- *
203 * Arguments: @sym_env *e@ = pointer to environment variable block
204 * @const char *val@ = value to set
208 * Use: Sets an environment variable block to the right value.
211 static void bc__setenv(sym_env
*e
, const char *val
)
213 e
->val
= xmalloc(strlen(e
->_base
.name
) + 1 + strlen(val
) + 1);
214 sprintf(e
->val
, "%s=%s", e
->_base
.name
, val
);
217 /* --- @bc__putenv@ --- *
219 * Arguments: @const char *var@ = name of the variable to set, or 0 if
220 * this is embedded in the value string
221 * @const char *val@ = value to set, or 0 if the variable must
223 * @unsigned int fl@ = flags to set
224 * @unsigned int force@ = force overwrite of preserved variables
226 * Returns: Pointer to symbol block, or zero if it was deleted.
228 * Use: Puts an item into the environment.
231 static sym_env
*bc__putenv(const char *var
, const char *val
,
232 unsigned int fl
, unsigned int force
)
239 /* --- Sort out embedded variable names --- */
242 const char *p
= strchr(val
, '=');
257 /* --- Find the variable block --- */
260 e
= sym_find(&bc__env
, var
, -1, sizeof(*e
), &f
);
261 if (!f
|| ~e
->f
& envFlag_preserve
|| force
) {
267 e
= sym_find(&bc__env
, var
, -1, 0, 0);
268 if (e
&& (force
|| ~e
->f
& envFlag_preserve
))
269 sym_remove(&bc__env
, e
);
273 /* --- Tidy up and return --- */
282 /* --- @bc__addGroups@ --- *
284 * Arguments: @gid_t *g@ = pointer to a group array
285 * @int *png@ = pointer to number of entries in the array
286 * @const gid_t *a@ = pointer to groups to add
287 * @int na@ = number of groups to add
289 * Returns: Zero if it was OK, nonzero if we should stop now.
291 * Use: Adds groups to a groups array.
294 static int bc__addGroups(gid_t
*g
, int *png
, const gid_t
*a
, int na
)
299 for (i
= 0; i
< na
; i
++) {
301 /* --- Ensure this group isn't already in the list --- */
303 for (j
= 0; j
< ng
; j
++) {
308 /* --- See if there's room for more --- */
310 if (ng
>= NGROUPS_MAX
) {
311 moan("too many groups (system limit exceeded) -- some have been lost");
316 /* --- Add the group --- */
326 /* --- @bc__banner@ --- *
328 * Arguments: @FILE *fp@ = stream to write on
332 * Use: Writes a banner containing copyright information.
335 static void bc__banner(FILE *fp
)
337 bc__write(fp
, "$ version " VERSION
"\n");
340 /* --- @bc__usage@ --- *
342 * Arguments: @FILE *fp@ = stream to write on
346 * Use: Writes a terse reminder of command line syntax.
349 static void bc__usage(FILE *fp
)
353 " $ -c <shell-command> <user>\n"
354 " $ [<env-var>] <user> [<command> [<arguments>]...]\n"
355 " $ -d [-p <port>] [-f <config-file>]\n");
358 /* --- @bc__help@ --- *
360 * Arguments: @FILE *fp@ = stream to write on
361 * @int suid@ = whether we're running set-uid
365 * Use: Displays a help message for this excellent piece of software.
368 static void bc__help(FILE *fp
, int suid
)
375 "The `$' program allows you to run a process as another user.\n"
376 "If a command name is given, this is the process executed. If the `-c'\n"
377 "option is used, the process is assumed to be `/bin/sh'. If no command is\n"
378 "given, your default login shell is used.\n"
380 "Your user id, the user id you wish to become, the name of the process\n"
381 "you wish to run, and the identity of the current host are looked up to\n"
382 "ensure that you have permission to do this.\n"
384 "Note that logs are kept of all uses of this program.\n"
386 "Options available are:\n"
388 "-h, --help Display this help text\n"
389 "-u, --usage Display a short usage summary\n"
390 "-v, --version Display $'s version number\n"
392 "-e, --preserve-environment Try to preserve the current environment\n"
393 "-s, --su, --set-user Set environment variables to reflect USER\n"
394 "-l, --login Really log in as USER\n"
396 "-g GROUP, --group=GROUP Set primary group-id to be GROUP\n"
397 #ifdef HAVE_SETGROUPS
398 "-k, --keep-groups Keep your current set of groups\n"
399 "-m, --merge-groups Merge the lists of groups\n"
400 "-r, --replace-groups Replace the list of groups\n"
403 "-c CMD, --command=CMD Run the (Bourne) shell command CMD\n"
405 "-d, --daemon Start a daemon\n"
406 "-p PORT, --port=PORT In daemon mode, listen on PORT\n"
407 "-f FILE, --config-file=FILE In daemon mode, read config from FILE\n");
412 "-I USER, --impersonate=USER Claim to be USER when asking the server\n");
415 "-T FILE, --trace=FILE Dump trace information to FILE (boring)\n"
416 "-L OPTS, --trace-level=OPTS Set level of tracing information\n");
422 * Arguments: @int argc@ = number of command line arguments
423 * @char *argv[]@ = pointer to the various arguments
425 * Returns: Zero if successful.
427 * Use: Allows a user to change UID.
430 int main(int argc
, char *argv
[])
432 /* --- Request block setup parameters --- */
434 request rq
; /* Request buffer to build */
435 char *cmd
= 0; /* Shell command to execute */
436 char *binary
= "/bin/sh"; /* Default binary to execute */
437 char **env
= environ
; /* Default environment to pass */
438 char **todo
= 0; /* Pointer to argument list */
439 char *who
= 0; /* Who we're meant to become */
440 struct passwd
*from_pw
= 0; /* User we are right now */
441 struct passwd
*to_pw
= 0; /* User we want to become */
443 /* --- Become server setup parameters --- */
445 char *conffile
= file_RULES
; /* Default config file for daemon */
446 int port
= 0; /* Default port for daemon */
448 /* --- Miscellanous shared variables --- */
450 unsigned flags
= 0; /* Various useful flags */
451 int style
= DEFAULT_LOGIN_STYLE
; /* Login style */
452 gid_t group
= -1; /* Default group to set */
453 int gstyle
= g_unset
; /* No group style set yet */
455 #ifdef HAVE_SETGROUPS
456 gid_t groups
[NGROUPS_MAX
]; /* Set of groups */
457 int ngroups
; /* Number of groups in the set */
460 /* --- Default argument list executes a shell command --- */
462 static char *shell
[] = {
463 "/bin/sh", /* Bourne shell */
464 "-c", /* Read from command line */
465 0, /* Pointer to shell command */
469 /* --- Definitions for the various flags --- */
472 f_daemon
= 1, /* Start up in daemon mode */
473 f_duff
= 2, /* Fault in arguments */
474 f_login
= 4, /* Execute as a login shell */
475 f_dummy
= 8, /* Don't actually do anything */
476 f_setuid
= 16, /* We're running setuid */
477 f_havegroup
= 32 /* Set a default group */
480 /* --- Set up the program name --- */
484 if (getuid() != geteuid())
487 /* --- Read the environment into a hashtable --- */
492 sym_createTable(&bc__env
);
493 for (p
= environ
; *p
; p
++)
494 bc__putenv(0, *p
, 0, 0);
497 /* --- Parse some command line arguments --- */
501 static struct option opts
[] = {
503 /* --- Asking for help --- */
505 { "help", 0, 0, 'h' },
506 { "usage", 0, 0, 'u' },
507 { "version", 0, 0, 'v' },
509 /* --- Login style options --- */
511 { "preserve-environment", 0, 0, 'e' },
513 { "set-user", 0, 0, 's' },
514 { "login", 0, 0, 'l' },
516 /* --- Group style options --- */
518 { "group", gFlag_argReq
, 0, 'g' },
519 #ifdef HAVE_SETGROUPS
520 { "keep-groups", 0, 0, 'k' },
521 { "merge-groups", 0, 0, 'm' },
522 { "replace-groups", 0, 0, 'r' },
525 /* --- Command to run options --- */
527 { "command", gFlag_argReq
, 0, 'c' },
529 /* --- Server options --- */
531 { "daemon", 0, 0, 'd' },
532 { "port", gFlag_argReq
, 0, 'p' },
533 { "config-file", gFlag_argReq
, 0, 'f' },
535 /* --- Tracing options --- */
538 { "impersonate", gFlag_argReq
, 0, 'I' },
539 { "trace", gFlag_argOpt
, 0, 'T' },
540 { "trace-level", gFlag_argOpt
, 0, 'L' },
546 i
= mdwopt(argc
, argv
,
547 "-" /* Return non-options as options */
548 "huv" /* Asking for help */
549 "esl" /* Login style options */
550 #ifdef HAVE_SETGROUPS
551 "g:kmr" /* Group style options */
553 "g:" /* Group (without @setgroups@) */
555 "c:" /* Command to run options */
556 "dp:f:" /* Server options */
558 "I:T::L::" /* Tracing options */
561 opts
, 0, 0, gFlag_envVar
);
567 /* --- Asking for help --- */
570 bc__help(stdout
, flags
& f_setuid
);
582 /* --- Login style options --- */
594 /* --- Group style options --- */
597 if (isdigit((unsigned char)optarg
[0]))
598 group
= atoi(optarg
);
600 struct group
*gr
= getgrnam(optarg
);
602 die("unknown group `%s'", optarg
);
605 flags
|= f_havegroup
;
618 /* --- Command to run options --- */
624 /* --- Server options --- */
627 if (isdigit((unsigned char)optarg
[0]))
628 port
= htons(atoi(optarg
));
630 struct servent
*s
= getservbyname(optarg
, "udp");
632 die("unknown service name `%s'", optarg
);
643 /* --- Pretend to be a different user --- *
645 * There are all sorts of nasty implications for this option. Don't
646 * allow it if we're running setuid. Disable the actual login anyway.
652 if (flags
& f_setuid
)
653 moan("shan't allow impersonation while running setuid");
656 if (isdigit((unsigned char)optarg
[0]))
657 pw
= getpwuid(atoi(optarg
));
659 pw
= getpwnam(optarg
);
661 die("can't impersonate unknown user `%s'", optarg
);
662 from_pw
= userdb_copyUser(pw
);
663 rq
.from
= from_pw
->pw_uid
;
670 /* --- Tracing support --- *
672 * Be careful not to zap a file I wouldn't normally be allowed to write
681 if (optarg
== 0 || strcmp(optarg
, "-") == 0)
684 uid_t eu
= geteuid(), ru
= getuid();
687 if (setreuid(eu
, ru
))
692 die("couldn't temporarily give up privileges: %s",
696 if ((fp
= fopen(optarg
, "w")) == 0) {
697 die("couldn't open trace file `%s' for writing: %s",
698 optarg
, strerror(errno
));
702 if (setreuid(ru
, eu
))
706 die("couldn't regain privileges: %s", strerror(errno
));
708 traceon(fp
, TRACE_DFL
);
709 trace(TRACE_MISC
, "become: tracing enabled");
714 /* --- Setting trace levels --- */
720 unsigned int lvl
= 0, l
;
721 const char *p
= optarg
;
723 /* --- Table of tracing facilities --- */
731 static tr lvltbl
[] = {
732 { 'm', TRACE_MISC
, "miscellaneous messages" },
733 { 's', TRACE_SETUP
, "building the request block" },
734 { 'd', TRACE_DAEMON
, "server process" },
735 { 'r', TRACE_RULE
, "ruleset scanning" },
736 { 'c', TRACE_CHECK
, "request checking" },
737 { 'l', TRACE_CLIENT
, "client process" },
738 { 'R', TRACE_RAND
, "random number generator" },
739 { 'C', TRACE_CRYPTO
, "cryptographic processing of requests" },
740 { 'y', TRACE_YACC
, "parsing configuration file" },
741 { 'D', TRACE_DFL
, "default tracing options" },
742 { 'A', TRACE_ALL
, "all tracing options" },
747 /* --- Output some help if there's no arguemnt --- */
755 for (tp
= lvltbl
; tp
->l
; tp
++) {
756 if ((flags
& f_setuid
) == 0 || tp
->l
& ~TRACE_PRIV
)
757 printf("%c -- %s\n", tp
->ch
, tp
->help
);
761 "Also, `+' and `-' options are recognised to turn on and off various\n"
762 "tracing options. For example, `A-r' enables everything except ruleset\n"
763 "tracing, and `A-D+c' is everything except the defaults, but with request\n"
775 for (tp
= lvltbl
; tp
->l
&& *p
!= tp
->ch
; tp
++)
778 if (flags
& f_setuid
)
781 lvl
= sense ?
(lvl
| l
) : (lvl
& ~l
);
783 moan("unknown trace option `%c'", *p
);
789 yydebug
= ((lvl
& TRACE_YACC
) != 0);
794 /* --- Something that wasn't an option --- *
796 * The following nasties are supported:
798 * * NAME=VALUE -- preserve NAME, and give it a VALUE
799 * * NAME= -- preserve NAME, and give it an empty value
800 * * NAME- -- delete NAME
801 * * NAME! -- preserve NAME with existing value
803 * Anything else is either the user name (which is OK) or the start of
804 * the command (in which case I stop and read the rest of the command).
808 size_t sz
= strcspn(optarg
, "=-!");
811 /* --- None of the above --- */
813 if (optarg
[sz
] == 0 || (optarg
[sz
] != '=' && optarg
[sz
+ 1] != 0)) {
823 /* --- Do the appropriate thing --- */
825 switch (optarg
[sz
]) {
827 bc__putenv(0, optarg
, envFlag_preserve
, 1);
831 bc__putenv(optarg
, 0, 0, 1);
835 if ((e
= sym_find(&bc__env
, optarg
, -1, 0, 0)) != 0)
836 e
->f
|= envFlag_preserve
;
841 /* --- Something I didn't understand has occurred --- */
850 if (flags
& f_duff
) {
855 /* --- Switch to daemon mode if requested --- */
857 if (flags
& f_daemon
) {
858 T( trace(TRACE_MISC
, "become: daemon mode requested"); )
859 daemon_init(conffile
, port
);
863 /* --- Open a syslog --- */
865 openlog(quis(), 0, LOG_AUTH
);
867 /* --- Pick out the uid --- */
877 if (isdigit((unsigned char)who
[0]))
878 pw
= getpwuid(atoi(who
));
882 die("unknown user `%s'", who
);
883 to_pw
= userdb_copyUser(pw
);
887 /* --- Fill in the easy bits of the request --- */
893 pw
= getpwuid(rq
.from
);
895 die("who are you? (can't find user %li)", (long)rq
.from
);
896 from_pw
= userdb_copyUser(pw
);
899 /* --- Find the local host address --- */
905 if ((he
= gethostbyname(u
.nodename
)) == 0)
906 die("who am I? (can't resolve `%s')", u
.nodename
);
907 memcpy(&rq
.host
, he
->h_addr
, sizeof(struct in_addr
));
910 /* --- Fiddle with group ownerships a bit --- */
913 #ifdef HAVE_SETGROUPS
914 gid_t from_gr
[NGROUPS_MAX
], to_gr
[NGROUPS_MAX
];
918 /* --- Set the default login group, if there is one --- */
920 if (~flags
& f_havegroup
)
921 group
= (style
== l_preserve
) ? from_pw
->pw_gid
: to_pw
->pw_gid
;
923 #ifndef HAVE_SETGROUPS
925 /* --- Check that it's valid --- */
927 if (group
!= from_pw
->pw_gid
&& group
!= to_pw
->pw_gid
)
928 die("invalid default group");
932 /* --- Set the default group style --- */
934 if (gstyle
== g_unset
)
935 gstyle
= (style
== l_login
) ? g_replace
: g_merge
;
937 /* --- Read in my current set of groups --- */
939 n_fgr
= getgroups(NGROUPS_MAX
, from_gr
);
941 /* --- Now read the groups for the target user --- *
943 * Do this even if I'm using the @g_keep@ style -- I'll use it for
944 * checking that @group@ is valid.
952 to_gr
[n_tgr
++] = to_pw
->pw_gid
;
955 while ((gr
= getgrent()) != 0) {
956 if (gr
->gr_gid
== to_gr
[0])
958 for (p
= gr
->gr_mem
; *p
; p
++) {
959 if (strcmp(to_pw
->pw_name
, *p
) == 0) {
960 to_gr
[n_tgr
++] = gr
->gr_gid
;
961 if (n_tgr
>= NGROUPS_MAX
)
972 /* --- Check that @group@ is reasonable --- */
977 if (group
== getgid() || group
== from_pw
->pw_gid
)
979 for (i
= 0; i
< n_fgr
; i
++) {
980 if (group
== from_gr
[i
])
983 for (i
= 0; i
< n_tgr
; i
++) {
984 if (group
== to_gr
[i
])
987 die("invalid default group");
991 /* --- Phew. Now comes the hard bit --- */
999 if (gstyle
& g_keep
) {
1001 ga
[i
++] = from_pw
->pw_gid
;
1003 if (gstyle
& g_replace
)
1004 ga
[i
++] = to_pw
->pw_gid
;
1006 /* --- Style authorities will become apoplectic if shown this --- *
1008 * As far as I can see, it's the neatest way of writing it.
1012 (void)(bc__addGroups(groups
, &ngroups
, ga
, i
) ||
1013 ((gstyle
& g_keep
) &&
1014 bc__addGroups(groups
, &ngroups
, from_gr
,n_fgr
)) ||
1015 ((gstyle
& g_replace
) &&
1016 bc__addGroups(groups
, &ngroups
, to_gr
, n_tgr
)));
1021 /* --- Trace the results of all this --- */
1023 T( trace(TRACE_SETUP
, "setup: default group == %i", (int)group
); )
1025 #ifdef HAVE_SETGROUPS
1026 IF_TRACING(TRACE_SETUP
, {
1029 for (i
= 1; i
< ngroups
; i
++)
1030 trace(TRACE_SETUP
, "setup: subsidiary group %i", (int)groups
[i
]);
1035 /* --- Shell commands are easy --- */
1042 /* --- A command given on the command line isn't too hard --- */
1044 else if (optind
< argc
) {
1045 todo
= argv
+ optind
;
1049 else switch (style
) {
1051 /* --- An unadorned becoming requires little work --- */
1054 shell
[0] = getenv("SHELL");
1056 shell
[0] = from_pw
->pw_shell
;
1062 /* --- An su-like login needs slightly less effort --- */
1065 shell
[0] = to_pw
->pw_shell
;
1071 /* --- A login request needs a little bit of work --- */
1074 const char *p
= strrchr(to_pw
->pw_shell
, '/');
1079 p
= to_pw
->pw_shell
;
1080 shell
[0] = xmalloc(strlen(p
) + 2);
1082 strcpy(shell
[0] + 1, p
);
1085 binary
= to_pw
->pw_shell
;
1086 chdir(to_pw
->pw_dir
);
1090 /* --- Mangle the environment --- *
1092 * This keeps getting more complicated all the time. (How true. Now I've
1093 * got all sorts of nasty environment mangling to do.)
1095 * The environment stuff now happens in seven phases:
1097 * 1. Mark very special variables to be preserved. Currently only TERM
1098 * and DISPLAY are treated in this way.
1100 * 2. Set and preserve Become's own environment variables.
1102 * 3. Set and preserve the user identity variables (USER, LOGNAME, HOME,
1103 * SHELL and MAIL) if we're being `su'-like or `login'-like.
1105 * 4. If we're preserving the environment or being `su'-like, process the
1106 * PATH variable a little. Otherwise reset it to something
1109 * 5. If we're being `login'-like, expunge all unpreserved variables.
1111 * 6. Expunge any security-critical variables.
1113 * 7. Build a new environment table to pass to child processes.
1117 /* --- Variables to be preserved always --- *
1119 * A user can explicitly expunge a variable in this list, in which case
1120 * we never get to see it here.
1123 static char *preserve
[] = {
1124 "TERM", "DISPLAY", 0
1127 /* --- Variables to be expunged --- *
1129 * Any environment string which has one of the following as a prefix will
1130 * be expunged from the environment passed to the called process. The
1131 * first line lists variables which have been used to list search paths
1132 * for shared libraries: by manipulating these, an attacker could replace
1133 * a standard library with one of his own. The second line lists other
1134 * well-known dangerous environment variables.
1137 static char *banned
[] = {
1138 "-LD_", "SHLIB_PATH", "LIBPATH", "-_RLD_",
1139 "IFS", "ENV", "BASH_ENV", "KRB_CONF",
1143 /* --- Other useful variables --- */
1152 /* --- Stage one. Preserve display-specific variables --- */
1154 for (pp
= preserve
; *pp
; pp
++) {
1155 if ((e
= sym_find(&bc__env
, *pp
, -1, 0, 0)) != 0)
1156 e
->f
|= envFlag_preserve
;
1159 /* --- Stage two. Set Become's own variables --- */
1161 e
= sym_find(&bc__env
, "BECOME_ORIGINAL_USER", -1, sizeof(*e
), &f
);
1163 bc__setenv(e
, from_pw
->pw_name
);
1164 e
->f
|= envFlag_preserve
;
1166 e
= sym_find(&bc__env
, "BECOME_ORIGINAL_HOME", -1, sizeof(*e
), &f
);
1168 bc__setenv(e
, from_pw
->pw_dir
);
1169 e
->f
|= envFlag_preserve
;
1171 bc__putenv("BECOME_OLD_USER", from_pw
->pw_name
, envFlag_preserve
, 0);
1172 bc__putenv("BECOME_OLD_HOME", from_pw
->pw_dir
, envFlag_preserve
, 0);
1173 bc__putenv("BECOME_USER", to_pw
->pw_name
, envFlag_preserve
, 0);
1174 bc__putenv("BECOME_HOME", to_pw
->pw_dir
, envFlag_preserve
, 0);
1176 /* --- Stage three. Set user identity --- */
1180 static char *maildirs
[] = {
1181 "/var/spool/mail", "/var/mail",
1182 "/usr/spool/mail", "/usr/mail",
1188 for (pp
= maildirs
; *pp
; pp
++) {
1189 if (stat(*pp
, &s
) == 0 && S_ISDIR(s
.st_mode
)) {
1190 sprintf(b
, "%s/%s", *pp
, to_pw
->pw_name
);
1191 bc__putenv("MAIL", b
, envFlag_preserve
, 0);
1195 } /* Fall through */
1198 bc__putenv("USER", to_pw
->pw_name
, envFlag_preserve
, 0);
1199 bc__putenv("LOGNAME", to_pw
->pw_name
, envFlag_preserve
, 0);
1200 bc__putenv("HOME", to_pw
->pw_dir
, envFlag_preserve
, 0);
1201 bc__putenv("SHELL", to_pw
->pw_shell
, envFlag_preserve
, 0);
1205 /* --- Stage four. Set the user's PATH properly --- */
1208 /* --- Find an existing path --- *
1210 * If there's no path, or this is a login, then set a default path,
1211 * unless we're meant to preserve the existing one. Whew!
1214 e
= sym_find(&bc__env
, "PATH", -1, sizeof(*e
), &f
);
1216 if (!f
|| (style
== l_login
&& ~e
->f
& envFlag_preserve
)) {
1218 rq
.to ?
"/usr/bin:/bin" : "/usr/bin:/usr/sbin:/bin:/sbin",
1219 envFlag_preserve
, 0);
1222 /* --- Find the string --- */
1224 e
->f
= envFlag_preserve
;
1225 p
= strchr(e
->val
, '=') + 1;
1228 /* --- Write the new version to a dynamically allocated buffer --- */
1230 e
->val
= xmalloc(4 + 1 + strlen(p
) + 1);
1231 strcpy(e
->val
, "PATH=");
1234 for (p
= strtok(p
, ":"); p
; p
= strtok(0, ":")) {
1249 /* --- Stages five and six. Expunge variables and count numbers --- *
1251 * Folded together, so I only need one pass through the table. Also
1252 * count the number of variables needed at this time.
1257 for (sym_createIter(&i
, &bc__env
); (e
= sym_next(&i
)) != 0; ) {
1259 /* --- Login style expunges all unpreserved variables --- */
1261 if (style
== l_login
&& ~e
->f
& envFlag_preserve
)
1264 /* --- Otherwise just check the name against the list --- */
1266 for (pp
= banned
; *pp
; pp
++) {
1269 if (memcmp(e
->_base
.name
, p
, strlen(p
)) == 0)
1271 } else if (strcmp(e
->_base
.name
, *pp
) == 0)
1279 sym_remove(&bc__env
, e
);
1282 /* --- Stage seven. Build the new environment block --- */
1284 env
= qq
= xmalloc((sz
+ 1) * sizeof(*qq
));
1286 for (sym_createIter(&i
, &bc__env
); (e
= sym_next(&i
)) != 0; )
1291 /* --- Trace the command --- */
1293 IF_TRACING(TRACE_SETUP
, {
1296 trace(TRACE_SETUP
, "setup: from user %s to user %s",
1297 from_pw
->pw_name
, to_pw
->pw_name
);
1298 trace(TRACE_SETUP
, "setup: binary == `%s'", binary
);
1299 for (i
= 0; todo
[i
]; i
++)
1300 trace(TRACE_SETUP
, "setup: arg %i == `%s'", i
, todo
[i
]);
1301 for (i
= 0; env
[i
]; i
++)
1302 trace(TRACE_SETUP
, "setup: env %i == `%s'", i
, env
[i
]);
1305 /* --- If necessary, resolve the path to the command --- */
1307 if (!strchr(binary
, '/')) {
1311 if ((p
= getenv("PATH")) == 0)
1312 p
= "/bin:/usr/bin";
1315 for (p
= strtok(path
, ":"); p
; p
= strtok(0, ":")) {
1317 /* --- Check length of string before copying --- */
1319 if (strlen(p
) + strlen(binary
) + 2 > sizeof(rq
.cmd
))
1322 /* --- Now build the pathname and check it --- */
1324 sprintf(rq
.cmd
, "%s/%s", p
, todo
[0]);
1325 if (stat(rq
.cmd
, &st
) == 0 && /* Check it exists */
1326 st
.st_mode
& 0111 && /* Check it's executable */
1327 S_ISREG(st
.st_mode
)) /* Check it's a file */
1332 die("couldn't find `%s' in path", todo
[0]);
1336 T( trace(TRACE_SETUP
, "setup: resolve binary to `%s'", binary
); )
1338 /* --- Canonicalise the path string, if necessary --- */
1345 /* --- Insert current directory name if path not absolute --- */
1350 if (!getcwd(b
, sizeof(b
)))
1351 die("couldn't read current directory: %s", strerror(errno
));
1356 /* --- Now copy over characters from the path string --- */
1360 /* --- Check for buffer overflows here --- *
1362 * I write at most one byte per iteration so this is OK. Remember to
1363 * allow one for the null byte.
1366 if (p
>= b
+ sizeof(b
) - 1)
1367 die("internal error: buffer overflow while canonifying path");
1369 /* --- Reduce multiple slashes to just one --- */
1377 /* --- Handle dots in filenames --- *
1379 * @p[-1]@ is valid here, because if @*q@ is not a `/' then either
1380 * we've just stuck the current directory on the end of the buffer,
1381 * or we've just put something else on the end.
1384 else if (*q
== '.' && p
[-1] == '/') {
1386 /* --- A simple `./' just gets removed --- */
1388 if (q
[1] == 0 || q
[1] == '/') {
1394 /* --- A `../' needs to be peeled back to the previous `/' --- */
1396 if (q
[1] == '.' && (q
[2] == 0 || q
[2] == '/')) {
1399 while (p
> b
&& p
[-1] != '/')
1412 T( trace(TRACE_SETUP
, "setup: canonify binary to `%s'", rq
.cmd
); )
1414 /* --- Run the check --- *
1416 * If the user is already what she wants to be, then print a warning.
1417 * Then, if I was just going to spawn a shell, quit, to reduce user
1418 * confusion. Otherwise, do what was wanted anyway. Also, don't bother
1419 * checking if we're already root -- root can do anything anyway, and at
1420 * least this way we get some logging done, and offer a more friendly
1424 if (rq
.from
== rq
.to
) {
1425 moan("you already are `%s'!", to_pw
->pw_name
);
1426 if (!cmd
&& todo
== shell
) {
1427 moan("(to prevent confusion, I'm not spawning a shell)");
1431 int a
= (rq
.from
== 0) || check(&rq
);
1434 "permission %s for %s to become %s to run `%s'",
1435 a ?
"granted" : "denied", from_pw
->pw_name
, to_pw
->pw_name
,
1439 die("permission denied");
1442 /* --- Now do the job --- */
1444 T( trace(TRACE_MISC
, "become: permission granted"); )
1446 if (flags
& f_dummy
) {
1447 puts("permission granted");
1451 #ifdef HAVE_SETGROUPS
1452 if (setgroups(ngroups
, groups
) < 0)
1453 die("couldn't set groups: %s", strerror(errno
));
1456 if (setgid(group
) < 0)
1457 die("couldn't set default group: %s", strerror(errno
));
1458 if (setuid(rq
.to
) < 0)
1459 die("couldn't set uid: %s", strerror(errno
));
1461 /* --- Finally, call the program --- */
1464 execve(rq
.cmd
, todo
, env
);
1465 die("couldn't exec `%s': %s", rq
.cmd
, strerror(errno
));
1469 /*----- That's all, folks -------------------------------------------------*/