Cosmetic change: use sizeof(destination) in memcpy.
[become] / src / become.c
CommitLineData
c4f2d992 1/* -*-c-*-
2 *
7df38e1e 3 * $Id: become.c,v 1.18 1998/06/26 10:32:54 mdw Exp $
c4f2d992 4 *
5 * Main code for `become'
6 *
c758e654 7 * (c) 1998 EBI
c4f2d992 8 */
9
03f996bd 10/*----- Licensing notice --------------------------------------------------*
c4f2d992 11 *
12 * This file is part of `become'
13 *
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.
18 *
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.
23 *
24 * You should have received a copy of the GNU General Public License
03f996bd 25 * along with `become'; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
c4f2d992 27 */
28
29/*----- Revision history --------------------------------------------------*
30 *
31 * $Log: become.c,v $
7df38e1e 32 * Revision 1.18 1998/06/26 10:32:54 mdw
33 * Cosmetic change: use sizeof(destination) in memcpy.
34 *
46a5b3c6 35 * Revision 1.17 1998/06/18 15:06:59 mdw
36 * Close log before execing program to avoid leaving a socket open.
37 *
f175e71b 38 * Revision 1.16 1998/04/23 13:21:04 mdw
39 * Small tweaks. Support no-network configuration option, and rearrange
40 * the help text a little.
41 *
94455fbb 42 * Revision 1.15 1998/01/13 11:10:44 mdw
43 * Add `TZ' to the list of variables to be preserved.
44 *
c758e654 45 * Revision 1.14 1998/01/12 16:45:39 mdw
46 * Fix copyright date.
47 *
9e5602f0 48 * Revision 1.13 1997/09/26 09:14:57 mdw
49 * Merged blowfish branch into trunk.
50 *
45df69ab 51 * Revision 1.12 1997/09/25 16:04:48 mdw
52 * Change directory after becoming someone else, instead of before. This
53 * avoids problems with root-squashed NFS mounts.
54 *
9e5602f0 55 * Revision 1.11.2.1 1997/09/26 09:07:58 mdw
56 * Use the Blowfish encryption algorithm instead of IDEA. This is partly
57 * because I prefer Blowfish (without any particularly strong evidence) but
58 * mainly because IDEA is patented and Blowfish isn't.
59 *
45df69ab 60 * Revision 1.11 1997/09/24 09:48:45 mdw
505f2736 61 * Fix (scary) overrun bug in group allocation stuff.
62 *
63 * Revision 1.10 1997/09/17 10:14:10 mdw
9df0c75d 64 * Fix a typo. Support service names in `--port' option.
65 *
a5089273 66 * Revision 1.9 1997/09/10 10:28:05 mdw
67 * Allow default port to be given as a service name or port number. Handle
68 * groups properly (lots of options here).
69 *
70 * Revision 1.8 1997/09/08 13:56:24 mdw
6a4ec25c 71 * Change criteria for expunging items from the user's PATH: instead of
72 * removing things starting with `.', remove things not starting with `/'.
73 *
74 * Revision 1.7 1997/09/08 13:43:20 mdw
c6885d5f 75 * Change userid when creating tracefiles rather than fiddling with
76 * `access': it works rather better. Also, insert some stdio buffer
77 * flushing to ensure tracedumps are completely written.
78 *
79 * Revision 1.6 1997/09/05 13:47:44 mdw
d6683a48 80 * Make the `-L' (trace-level) option's argument optional, like the long
81 * version is.
82 *
83 * Revision 1.5 1997/09/05 11:45:19 mdw
387501ae 84 * Add support for different login styles, and environment variable
85 * manipulation in a safe and useful way.
86 *
87 * Revision 1.4 1997/08/20 16:15:13 mdw
569b8891 88 * Overhaul of environment handling. Fix daft bug in path search code.
89 *
88e486d5 90 * Revision 1.3 1997/08/07 16:28:59 mdw
91 * Do something useful when users attempt to become themselves.
92 *
03f996bd 93 * Revision 1.2 1997/08/04 10:24:20 mdw
94 * Sources placed under CVS control.
95 *
96 * Revision 1.1 1997/07/21 13:47:54 mdw
c4f2d992 97 * Initial revision
98 *
99 */
100
101/*----- Header files ------------------------------------------------------*/
102
103/* --- ANSI headers --- */
104
105#include <ctype.h>
106#include <errno.h>
a5089273 107#include <limits.h>
c4f2d992 108#include <stdio.h>
109#include <stdlib.h>
110#include <string.h>
03f996bd 111#include <time.h>
c4f2d992 112
113/* --- Unix headers --- */
114
115#include <sys/types.h>
116#include <sys/stat.h>
117#include <sys/socket.h>
118#include <sys/utsname.h>
119
120#include <netinet/in.h>
121
122#include <arpa/inet.h>
123
124#include <netdb.h>
a5089273 125#include <grp.h>
c4f2d992 126#include <pwd.h>
127#include <syslog.h>
128#include <unistd.h>
129
03f996bd 130extern char **environ;
131
c4f2d992 132/* --- Local headers --- */
133
134#include "become.h"
135#include "config.h"
136#include "check.h"
137#include "daemon.h"
138#include "lexer.h"
139#include "mdwopt.h"
140#include "name.h"
141#include "parser.h"
142#include "rule.h"
387501ae 143#include "sym.h"
c4f2d992 144#include "utils.h"
03f996bd 145#include "userdb.h"
c4f2d992 146
387501ae 147/*----- Type definitions --------------------------------------------------*/
148
149/* --- Symbol table entry for an environment variable --- */
150
151typedef struct sym_env {
152 sym_base _base; /* Symbol table information */
153 unsigned f; /* Flags word (see below) */
154 char *val; /* Pointer to variable value */
155} sym_env;
156
157/* --- Environment variable flags --- */
158
159enum {
160 envFlag_preserve = 1
161};
162
163/* --- Login behaviour types --- */
164
f175e71b 165#define l_preserve 0 /* Preserve the environment */
166#define l_setuser 1 /* Update who I am */
167#define l_login 2 /* Do a full login */
387501ae 168
a5089273 169/* --- Group behaviour types --- *
170 *
171 * Note that these make a handy bitfield.
172 */
173
174#ifdef HAVE_SETGROUPS
175
176enum {
177 g_unset = 0, /* Nobody's set a preference */
178 g_keep = 1, /* Leave the group memberships */
179 g_replace = 2, /* Replace group memberships */
180 g_merge = (g_keep | g_replace) /* Merge the group memberships */
181};
182
183#endif
184
387501ae 185/*----- Static variables --------------------------------------------------*/
186
187static sym_table bc__env;
188
c4f2d992 189/*----- Main code ---------------------------------------------------------*/
190
191/* --- @bc__write@ --- *
192 *
193 * Arguments: @FILE *fp@ = pointer to a stream to write on
194 * @const char *p@ = pointer to a string
195 *
196 * Returns: ---
197 *
198 * Use: Writes the string to the stream, substituting the program
199 * name (as returned by @quis@) for each occurrence of the
200 * character `$'.
201 */
202
203static void bc__write(FILE *fp, const char *p)
204{
205 const char *n = quis();
206 size_t l;
207 size_t nl = strlen(n);
208
209 /* --- Try to be a little efficient --- *
210 *
569b8891 211 * Gather up non-`$' characters using @strcspn@ and spew them out really
c4f2d992 212 * quickly.
213 */
214
215 for (;;) {
216 l = strcspn(p, "$");
217 if (l)
218 fwrite(p, l, 1, fp);
219 p += l;
220 if (!*p)
221 break;
222 fwrite(n, nl, 1, fp);
223 p++;
224 }
225}
226
387501ae 227/* --- @bc__setenv@ --- *
228 *
229 * Arguments: @sym_env *e@ = pointer to environment variable block
230 * @const char *val@ = value to set
231 *
232 * Returns: ---
233 *
234 * Use: Sets an environment variable block to the right value.
235 */
236
237static void bc__setenv(sym_env *e, const char *val)
238{
239 e->val = xmalloc(strlen(e->_base.name) + 1 + strlen(val) + 1);
240 sprintf(e->val, "%s=%s", e->_base.name, val);
241}
242
243/* --- @bc__putenv@ --- *
244 *
245 * Arguments: @const char *var@ = name of the variable to set, or 0 if
246 * this is embedded in the value string
247 * @const char *val@ = value to set, or 0 if the variable must
248 * be removed
249 * @unsigned int fl@ = flags to set
250 * @unsigned int force@ = force overwrite of preserved variables
251 *
252 * Returns: Pointer to symbol block, or zero if it was deleted.
253 *
254 * Use: Puts an item into the environment.
255 */
256
257static sym_env *bc__putenv(const char *var, const char *val,
258 unsigned int fl, unsigned int force)
259{
260 unsigned int f;
261 sym_env *e;
262 char *q = 0;
263 size_t sz;
264
265 /* --- Sort out embedded variable names --- */
266
267 if (!var) {
268 const char *p = strchr(val, '=');
269
270 if (p) {
271 sz = p - val;
272 q = xmalloc(sz + 1);
273 memcpy(q, val, sz);
274 q[sz] = 0;
275 var = q;
276 val = p + 1;
277 } else {
278 var = val;
279 val = 0;
280 }
281 }
282
283 /* --- Find the variable block --- */
284
285 if (val) {
286 e = sym_find(&bc__env, var, -1, sizeof(*e), &f);
a5089273 287 if (!f || ~e->f & envFlag_preserve || force) {
387501ae 288 if (f)
289 free(e->val);
290 bc__setenv(e, val);
291 }
292 } else {
293 e = sym_find(&bc__env, var, -1, 0, 0);
294 if (e && (force || ~e->f & envFlag_preserve))
295 sym_remove(&bc__env, e);
296 e = 0;
297 }
298
299 /* --- Tidy up and return --- */
300
301 if (q)
302 free(q);
303 if (e)
304 e->f = fl;
305 return (e);
306}
307
a5089273 308/* --- @bc__addGroups@ --- *
309 *
310 * Arguments: @gid_t *g@ = pointer to a group array
311 * @int *png@ = pointer to number of entries in the array
312 * @const gid_t *a@ = pointer to groups to add
313 * @int na@ = number of groups to add
314 *
315 * Returns: Zero if it was OK, nonzero if we should stop now.
316 *
317 * Use: Adds groups to a groups array.
318 */
319
320static int bc__addGroups(gid_t *g, int *png, const gid_t *a, int na)
321{
322 int i, j;
323 int ng = *png;
324
325 for (i = 0; i < na; i++) {
326
327 /* --- Ensure this group isn't already in the list --- */
328
329 for (j = 0; j < ng; j++) {
330 if (a[i] == g[j])
331 goto next_group;
332 }
333
334 /* --- See if there's room for more --- */
335
505f2736 336 if (ng >= NGROUPS_MAX) {
337 moan("too many groups (system limit exceeded) -- some have been lost");
a5089273 338 *png = ng;
339 return (-1);
340 }
341
342 /* --- Add the group --- */
343
344 g[ng++] = a[i];
345 next_group:;
346 }
347
348 *png = ng;
349 return (0);
350}
351
c4f2d992 352/* --- @bc__banner@ --- *
353 *
354 * Arguments: @FILE *fp@ = stream to write on
355 *
356 * Returns: ---
357 *
358 * Use: Writes a banner containing copyright information.
359 */
360
361static void bc__banner(FILE *fp)
362{
363 bc__write(fp, "$ version " VERSION "\n");
364}
365
366/* --- @bc__usage@ --- *
367 *
368 * Arguments: @FILE *fp@ = stream to write on
369 *
370 * Returns: ---
371 *
372 * Use: Writes a terse reminder of command line syntax.
373 */
374
375static void bc__usage(FILE *fp)
376{
377 bc__write(fp,
378 "Usage: \n"
379 " $ -c <shell-command> <user>\n"
387501ae 380 " $ [<env-var>] <user> [<command> [<arguments>]...]\n"
f175e71b 381#ifndef NONETWORK
382 " $ -d [-p <port>] [-f <config-file>]\n"
383#endif
384 );
c4f2d992 385}
386
387/* --- @bc__help@ --- *
388 *
389 * Arguments: @FILE *fp@ = stream to write on
569b8891 390 * @int suid@ = whether we're running set-uid
c4f2d992 391 *
392 * Returns: ---
393 *
394 * Use: Displays a help message for this excellent piece of software.
395 */
396
569b8891 397static void bc__help(FILE *fp, int suid)
c4f2d992 398{
399 bc__banner(fp);
400 putc('\n', fp);
401 bc__usage(fp);
402 putc('\n', fp);
403 bc__write(fp,
404"The `$' program allows you to run a process as another user.\n"
405"If a command name is given, this is the process executed. If the `-c'\n"
406"option is used, the process is assumed to be `/bin/sh'. If no command is\n"
407"given, your default login shell is used.\n"
408"\n"
409"Your user id, the user id you wish to become, the name of the process\n"
410"you wish to run, and the identity of the current host are looked up to\n"
411"ensure that you have permission to do this.\n"
412"\n"
413"Note that logs are kept of all uses of this program.\n"
414"\n"
415"Options available are:\n"
416"\n"
387501ae 417"-h, --help Display this help text\n"
418"-u, --usage Display a short usage summary\n"
419"-v, --version Display $'s version number\n"
420"\n"
421"-e, --preserve-environment Try to preserve the current environment\n"
422"-s, --su, --set-user Set environment variables to reflect USER\n"
423"-l, --login Really log in as USER\n"
f175e71b 424" [Default is "
425#if DEFAULT_LOGIN_STYLE == l_preserve
426 "preserve-environment"
427#elif DEFAULT_LOGIN_STYLE == l_setuser
428 "set-user"
429#elif DEFAULT_LOGIN_STYLE == l_login
430 "login"
431#else
432 "poorly configured"
433#endif
434"]\n\n"
a5089273 435"-g GROUP, --group=GROUP Set primary group-id to be GROUP\n"
436#ifdef HAVE_SETGROUPS
437"-k, --keep-groups Keep your current set of groups\n"
438"-m, --merge-groups Merge the lists of groups\n"
439"-r, --replace-groups Replace the list of groups\n"
440#endif
441"\n"
387501ae 442"-c CMD, --command=CMD Run the (Bourne) shell command CMD\n"
f175e71b 443#ifndef NONETWORK
387501ae 444"\n"
445"-d, --daemon Start a daemon\n"
446"-p PORT, --port=PORT In daemon mode, listen on PORT\n"
f175e71b 447"-f FILE, --config-file=FILE In daemon mode, read config from FILE\n"
448#endif
449 );
03f996bd 450#ifdef TRACING
387501ae 451 bc__write(fp, "\n");
569b8891 452 if (!suid) {
453 bc__write(fp,
387501ae 454"-I USER, --impersonate=USER Claim to be USER when asking the server\n");
569b8891 455 }
456 bc__write(fp,
387501ae 457"-T FILE, --trace=FILE Dump trace information to FILE (boring)\n"
458"-L OPTS, --trace-level=OPTS Set level of tracing information\n");
03f996bd 459#endif
c4f2d992 460}
461
462/* --- @main@ --- *
463 *
464 * Arguments: @int argc@ = number of command line arguments
465 * @char *argv[]@ = pointer to the various arguments
466 *
467 * Returns: Zero if successful.
468 *
469 * Use: Allows a user to change UID.
470 */
471
472int main(int argc, char *argv[])
473{
03f996bd 474 /* --- Request block setup parameters --- */
c4f2d992 475
03f996bd 476 request rq; /* Request buffer to build */
477 char *cmd = 0; /* Shell command to execute */
478 char *binary = "/bin/sh"; /* Default binary to execute */
479 char **env = environ; /* Default environment to pass */
387501ae 480 char **todo = 0; /* Pointer to argument list */
481 char *who = 0; /* Who we're meant to become */
03f996bd 482 struct passwd *from_pw = 0; /* User we are right now */
483 struct passwd *to_pw = 0; /* User we want to become */
484
485 /* --- Become server setup parameters --- */
486
f175e71b 487#ifndef NONETWORK
03f996bd 488 char *conffile = file_RULES; /* Default config file for daemon */
9df0c75d 489 int port = 0; /* Default port for daemon */
f175e71b 490#endif
03f996bd 491
492 /* --- Miscellanous shared variables --- */
493
494 unsigned flags = 0; /* Various useful flags */
387501ae 495 int style = DEFAULT_LOGIN_STYLE; /* Login style */
a5089273 496 gid_t group = -1; /* Default group to set */
497 int gstyle = g_unset; /* No group style set yet */
498
499#ifdef HAVE_SETGROUPS
500 gid_t groups[NGROUPS_MAX]; /* Set of groups */
501 int ngroups; /* Number of groups in the set */
502#endif
03f996bd 503
504 /* --- Default argument list executes a shell command --- */
505
506 static char *shell[] = {
507 "/bin/sh", /* Bourne shell */
508 "-c", /* Read from command line */
509 0, /* Pointer to shell command */
510 0 /* Terminator */
c4f2d992 511 };
512
03f996bd 513 /* --- Definitions for the various flags --- */
514
515 enum {
516 f_daemon = 1, /* Start up in daemon mode */
517 f_duff = 2, /* Fault in arguments */
45df69ab 518 f_shell = 4, /* Run a default shell */
03f996bd 519 f_dummy = 8, /* Don't actually do anything */
a5089273 520 f_setuid = 16, /* We're running setuid */
521 f_havegroup = 32 /* Set a default group */
03f996bd 522 };
c4f2d992 523
524 /* --- Set up the program name --- */
525
526 ego(argv[0]);
03f996bd 527 clock();
528 if (getuid() != geteuid())
529 flags |= f_setuid;
c4f2d992 530
387501ae 531 /* --- Read the environment into a hashtable --- */
532
533 {
534 char **p;
535
536 sym_createTable(&bc__env);
537 for (p = environ; *p; p++)
538 bc__putenv(0, *p, 0, 0);
539 }
540
c4f2d992 541 /* --- Parse some command line arguments --- */
542
543 for (;;) {
544 int i;
03f996bd 545 static struct option opts[] = {
387501ae 546
547 /* --- Asking for help --- */
548
c4f2d992 549 { "help", 0, 0, 'h' },
387501ae 550 { "usage", 0, 0, 'u' },
c4f2d992 551 { "version", 0, 0, 'v' },
387501ae 552
553 /* --- Login style options --- */
554
555 { "preserve-environment", 0, 0, 'e' },
556 { "su", 0, 0, 's' },
557 { "set-user", 0, 0, 's' },
03f996bd 558 { "login", 0, 0, 'l' },
387501ae 559
a5089273 560 /* --- Group style options --- */
561
562 { "group", gFlag_argReq, 0, 'g' },
563#ifdef HAVE_SETGROUPS
564 { "keep-groups", 0, 0, 'k' },
565 { "merge-groups", 0, 0, 'm' },
566 { "replace-groups", 0, 0, 'r' },
567#endif
568
387501ae 569 /* --- Command to run options --- */
570
c4f2d992 571 { "command", gFlag_argReq, 0, 'c' },
387501ae 572
573 /* --- Server options --- */
574
f175e71b 575#ifndef NONETWORK
c4f2d992 576 { "daemon", 0, 0, 'd' },
577 { "port", gFlag_argReq, 0, 'p' },
578 { "config-file", gFlag_argReq, 0, 'f' },
f175e71b 579#endif
387501ae 580
581 /* --- Tracing options --- */
582
03f996bd 583#ifdef TRACING
584 { "impersonate", gFlag_argReq, 0, 'I' },
585 { "trace", gFlag_argOpt, 0, 'T' },
586 { "trace-level", gFlag_argOpt, 0, 'L' },
587#endif
387501ae 588
c4f2d992 589 { 0, 0, 0, 0 }
590 };
591
387501ae 592 i = mdwopt(argc, argv,
a5089273 593 "-" /* Return non-options as options */
594 "huv" /* Asking for help */
595 "esl" /* Login style options */
596#ifdef HAVE_SETGROUPS
597 "g:kmr" /* Group style options */
598#else
599 "g:" /* Group (without @setgroups@) */
600#endif
601 "c:" /* Command to run options */
f175e71b 602#ifndef NONETWORK
a5089273 603 "dp:f:" /* Server options */
f175e71b 604#endif
a5089273 605#ifdef TRACING
606 "I:T::L::" /* Tracing options */
607#endif
608 ,
387501ae 609 opts, 0, 0, gFlag_envVar);
c4f2d992 610 if (i < 0)
387501ae 611 goto done_options;
c4f2d992 612
613 switch (i) {
03f996bd 614
387501ae 615 /* --- Asking for help --- */
03f996bd 616
c4f2d992 617 case 'h':
569b8891 618 bc__help(stdout, flags & f_setuid);
619 exit(0);
620 break;
387501ae 621 case 'u':
569b8891 622 bc__usage(stdout);
c4f2d992 623 exit(0);
624 break;
625 case 'v':
626 bc__banner(stdout);
627 exit(0);
628 break;
03f996bd 629
387501ae 630 /* --- Login style options --- */
03f996bd 631
387501ae 632 case 'e':
633 style = l_preserve;
634 break;
635 case 's':
a5089273 636 style = l_setuser;
387501ae 637 break;
03f996bd 638 case 'l':
387501ae 639 style = l_login;
03f996bd 640 break;
387501ae 641
a5089273 642 /* --- Group style options --- */
643
644 case 'g':
645 if (isdigit((unsigned char)optarg[0]))
646 group = atoi(optarg);
647 else {
648 struct group *gr = getgrnam(optarg);
649 if (!gr)
650 die("unknown group `%s'", optarg);
651 group = gr->gr_gid;
652 }
653 flags |= f_havegroup;
654 break;
655
656 case 'k':
657 gstyle = g_keep;
658 break;
659 case 'm':
660 gstyle = g_merge;
661 break;
662 case 'r':
663 gstyle = g_replace;
664 break;
665
387501ae 666 /* --- Command to run options --- */
667
c4f2d992 668 case 'c':
669 cmd = optarg;
670 break;
387501ae 671
672 /* --- Server options --- */
673
f175e71b 674#ifndef NONETWORK
c4f2d992 675 case 'p':
a5089273 676 if (isdigit((unsigned char)optarg[0]))
677 port = htons(atoi(optarg));
678 else {
679 struct servent *s = getservbyname(optarg, "udp");
680 if (!s)
681 die("unknown service name `%s'", optarg);
682 port = s->s_port;
683 }
c4f2d992 684 break;
685 case 'd':
686 flags |= f_daemon;
687 break;
688 case 'f':
689 conffile = optarg;
690 break;
f175e71b 691#endif
03f996bd 692
693 /* --- Pretend to be a different user --- *
694 *
695 * There are all sorts of nasty implications for this option. Don't
696 * allow it if we're running setuid. Disable the actual login anyway.
697 */
698
699#ifdef TRACING
569b8891 700
03f996bd 701 case 'I':
702 if (flags & f_setuid)
703 moan("shan't allow impersonation while running setuid");
704 else {
705 struct passwd *pw;
706 if (isdigit((unsigned char)optarg[0]))
707 pw = getpwuid(atoi(optarg));
708 else
709 pw = getpwnam(optarg);
710 if (!pw)
711 die("can't impersonate unknown user `%s'", optarg);
712 from_pw = userdb_copyUser(pw);
713 rq.from = from_pw->pw_uid;
714 flags |= f_dummy;
715 }
c4f2d992 716 break;
569b8891 717
03f996bd 718#endif
719
720 /* --- Tracing support --- *
721 *
722 * Be careful not to zap a file I wouldn't normally be allowed to write
723 * to!
724 */
725
726#ifdef TRACING
727
728 case 'T': {
729 FILE *fp;
730
731 if (optarg == 0 || strcmp(optarg, "-") == 0)
732 fp = stdout;
733 else {
c6885d5f 734 uid_t eu = geteuid(), ru = getuid();
735
736#ifdef HAVE_SETREUID
737 if (setreuid(eu, ru))
738#else
739 if (seteuid(ru))
740#endif
741 {
742 die("couldn't temporarily give up privileges: %s",
743 strerror(errno));
03f996bd 744 }
c6885d5f 745
03f996bd 746 if ((fp = fopen(optarg, "w")) == 0) {
747 die("couldn't open trace file `%s' for writing: %s",
748 optarg, strerror(errno));
749 }
c6885d5f 750
751#ifdef HAVE_SETREUID
752 if (setreuid(ru, eu))
753#else
754 if (seteuid(eu))
755#endif
756 die("couldn't regain privileges: %s", strerror(errno));
03f996bd 757 }
758 traceon(fp, TRACE_DFL);
759 trace(TRACE_MISC, "become: tracing enabled");
760 } break;
761
762#endif
763
764 /* --- Setting trace levels --- */
765
766#ifdef TRACING
767
768 case 'L': {
769 int sense = 1;
770 unsigned int lvl = 0, l;
771 const char *p = optarg;
772
773 /* --- Table of tracing facilities --- */
774
775 typedef struct tr {
776 char ch;
777 unsigned int l;
778 const char *help;
779 } tr;
780
781 static tr lvltbl[] = {
782 { 'm', TRACE_MISC, "miscellaneous messages" },
783 { 's', TRACE_SETUP, "building the request block" },
03f996bd 784 { 'r', TRACE_RULE, "ruleset scanning" },
785 { 'c', TRACE_CHECK, "request checking" },
f175e71b 786#ifndef NONETWORK
787 { 'd', TRACE_DAEMON, "server process" },
03f996bd 788 { 'l', TRACE_CLIENT, "client process" },
789 { 'R', TRACE_RAND, "random number generator" },
790 { 'C', TRACE_CRYPTO, "cryptographic processing of requests" },
f175e71b 791#endif
03f996bd 792 { 'y', TRACE_YACC, "parsing configuration file" },
793 { 'D', TRACE_DFL, "default tracing options" },
794 { 'A', TRACE_ALL, "all tracing options" },
795 { 0, 0, 0 }
796 };
797 tr *tp;
798
799 /* --- Output some help if there's no arguemnt --- */
800
801 if (!optarg) {
802 bc__banner(stdout);
803 bc__write(stdout,
804 "\n"
805 "Tracing options:\n"
806 "\n");
807 for (tp = lvltbl; tp->l; tp++) {
808 if ((flags & f_setuid) == 0 || tp->l & ~TRACE_PRIV)
809 printf("%c -- %s\n", tp->ch, tp->help);
810 }
811 bc__write(stdout,
812"\n"
9df0c75d 813"Also, `+' and `-' options are recognised to turn on and off various\n"
03f996bd 814"tracing options. For example, `A-r' enables everything except ruleset\n"
815"tracing, and `A-D+c' is everything except the defaults, but with request\n"
816"check tracing.\n"
817);
818 exit(0);
819 }
820
821 while (*p) {
822 if (*p == '+')
823 sense = 1;
824 else if (*p == '-')
825 sense = 0;
826 else {
827 for (tp = lvltbl; tp->l && *p != tp->ch; tp++)
828 ;
829 l = tp->l;
830 if (flags & f_setuid)
831 l &= ~TRACE_PRIV;
832 if (l)
833 lvl = sense ? (lvl | l) : (lvl & ~l);
834 else
835 moan("unknown trace option `%c'", *p);
836 }
837 p++;
838 }
839
840 tracesetlvl(lvl);
841 yydebug = ((lvl & TRACE_YACC) != 0);
842 } break;
843
844#endif
845
387501ae 846 /* --- Something that wasn't an option --- *
847 *
848 * The following nasties are supported:
849 *
850 * * NAME=VALUE -- preserve NAME, and give it a VALUE
851 * * NAME= -- preserve NAME, and give it an empty value
852 * * NAME- -- delete NAME
853 * * NAME! -- preserve NAME with existing value
854 *
855 * Anything else is either the user name (which is OK) or the start of
856 * the command (in which case I stop and read the rest of the command).
857 */
858
859 case 0: {
860 size_t sz = strcspn(optarg, "=-!");
861 sym_env *e;
862
863 /* --- None of the above --- */
864
865 if (optarg[sz] == 0 || (optarg[sz] != '=' && optarg[sz + 1] != 0)) {
9df0c75d 866 if (!who) {
387501ae 867 who = optarg;
9df0c75d 868 break;
869 } else {
387501ae 870 optind--;
871 goto done_options;
872 }
873 }
874
875 /* --- Do the appropriate thing --- */
876
877 switch (optarg[sz]) {
878 case '=':
879 bc__putenv(0, optarg, envFlag_preserve, 1);
880 break;
881 case '-':
882 optarg[sz] = 0;
883 bc__putenv(optarg, 0, 0, 1);
884 break;
885 case '!':
886 optarg[sz] = 0;
887 if ((e = sym_find(&bc__env, optarg, -1, 0, 0)) != 0)
888 e->f |= envFlag_preserve;
889 break;
890 }
891 } break;
892
03f996bd 893 /* --- Something I didn't understand has occurred --- */
894
c4f2d992 895 case '?':
896 flags |= f_duff;
897 break;
898 }
899 }
387501ae 900
901done_options:
c4f2d992 902 if (flags & f_duff) {
903 bc__usage(stderr);
904 exit(1);
905 }
906
907 /* --- Switch to daemon mode if requested --- */
908
f175e71b 909#ifndef NONETWORK
c4f2d992 910 if (flags & f_daemon) {
03f996bd 911 T( trace(TRACE_MISC, "become: daemon mode requested"); )
c4f2d992 912 daemon_init(conffile, port);
913 exit(0);
914 }
f175e71b 915#endif
c4f2d992 916
917 /* --- Open a syslog --- */
918
919 openlog(quis(), 0, LOG_AUTH);
920
921 /* --- Pick out the uid --- */
922
923 {
c4f2d992 924 struct passwd *pw;
03f996bd 925
387501ae 926 if (!who) {
c4f2d992 927 bc__usage(stderr);
928 exit(1);
929 }
03f996bd 930
387501ae 931 if (isdigit((unsigned char)who[0]))
932 pw = getpwuid(atoi(who));
03f996bd 933 else
387501ae 934 pw = getpwnam(who);
c4f2d992 935 if (!pw)
387501ae 936 die("unknown user `%s'", who);
03f996bd 937 to_pw = userdb_copyUser(pw);
c4f2d992 938 rq.to = pw->pw_uid;
939 }
940
941 /* --- Fill in the easy bits of the request --- */
942
03f996bd 943 if (!from_pw) {
944 struct passwd *pw;
945
946 rq.from = getuid();
947 pw = getpwuid(rq.from);
948 if (!pw)
949 die("who are you? (can't find user %li)", (long)rq.from);
950 from_pw = userdb_copyUser(pw);
951 }
c4f2d992 952
953 /* --- Find the local host address --- */
954
955 {
956 struct utsname u;
957 struct hostent *he;
958 uname(&u);
959 if ((he = gethostbyname(u.nodename)) == 0)
960 die("who am I? (can't resolve `%s')", u.nodename);
7df38e1e 961 memcpy(&rq.host, he->h_addr, sizeof(rq.host));
c4f2d992 962 }
963
a5089273 964 /* --- Fiddle with group ownerships a bit --- */
965
966 {
967#ifdef HAVE_SETGROUPS
968 gid_t from_gr[NGROUPS_MAX], to_gr[NGROUPS_MAX];
969 int n_fgr, n_tgr;
970#endif
971
972 /* --- Set the default login group, if there is one --- */
973
974 if (~flags & f_havegroup)
975 group = (style == l_preserve) ? from_pw->pw_gid : to_pw->pw_gid;
976
977#ifndef HAVE_SETGROUPS
978
979 /* --- Check that it's valid --- */
980
981 if (group != from_pw->pw_gid && group != to_pw->pw_gid)
982 die("invalid default group");
983
984#else
985
986 /* --- Set the default group style --- */
987
988 if (gstyle == g_unset)
989 gstyle = (style == l_login) ? g_replace : g_merge;
990
991 /* --- Read in my current set of groups --- */
992
993 n_fgr = getgroups(NGROUPS_MAX, from_gr);
994
995 /* --- Now read the groups for the target user --- *
996 *
997 * Do this even if I'm using the @g_keep@ style -- I'll use it for
998 * checking that @group@ is valid.
999 */
1000
1001 {
1002 struct group *gr;
1003 char **p;
1004
1005 n_tgr = 0;
1006 to_gr[n_tgr++] = to_pw->pw_gid;
1007
1008 setgrent();
1009 while ((gr = getgrent()) != 0) {
1010 if (gr->gr_gid == to_gr[0])
1011 continue;
1012 for (p = gr->gr_mem; *p; p++) {
1013 if (strcmp(to_pw->pw_name, *p) == 0) {
1014 to_gr[n_tgr++] = gr->gr_gid;
1015 if (n_tgr >= NGROUPS_MAX)
1016 goto done_groups;
1017 break;
1018 }
1019 }
1020 }
1021
1022 done_groups:
1023 endgrent();
1024 }
1025
1026 /* --- Check that @group@ is reasonable --- */
1027
1028 {
1029 int i;
1030
1031 if (group == getgid() || group == from_pw->pw_gid)
1032 goto group_ok;
1033 for (i = 0; i < n_fgr; i++) {
1034 if (group == from_gr[i])
1035 goto group_ok;
1036 }
1037 for (i = 0; i < n_tgr; i++) {
1038 if (group == to_gr[i])
1039 goto group_ok;
1040 }
1041 die("invalid default group");
1042 group_ok:;
1043 }
1044
1045 /* --- Phew. Now comes the hard bit --- */
1046
1047 {
1048 gid_t ga[4];
1049 int i;
1050
1051 i = 0;
1052 ga[i++] = group;
1053 if (gstyle & g_keep) {
1054 ga[i++] = getgid();
1055 ga[i++] = from_pw->pw_gid;
1056 }
1057 if (gstyle & g_replace)
1058 ga[i++] = to_pw->pw_gid;
1059
1060 /* --- Style authorities will become apoplectic if shown this --- *
1061 *
1062 * As far as I can see, it's the neatest way of writing it.
1063 */
1064
1065 ngroups = 0;
1066 (void)(bc__addGroups(groups, &ngroups, ga, i) ||
1067 ((gstyle & g_keep) &&
f175e71b 1068 bc__addGroups(groups, &ngroups, from_gr, n_fgr)) ||
a5089273 1069 ((gstyle & g_replace) &&
1070 bc__addGroups(groups, &ngroups, to_gr, n_tgr)));
1071 }
1072
1073#endif
1074
1075 /* --- Trace the results of all this --- */
1076
1077 T( trace(TRACE_SETUP, "setup: default group == %i", (int)group); )
1078
1079#ifdef HAVE_SETGROUPS
1080 IF_TRACING(TRACE_SETUP, {
1081 int i;
1082
1083 for (i = 1; i < ngroups; i++)
1084 trace(TRACE_SETUP, "setup: subsidiary group %i", (int)groups[i]);
1085 })
1086#endif
1087 }
1088
03f996bd 1089 /* --- Shell commands are easy --- */
c4f2d992 1090
1091 if (cmd) {
1092 shell[2] = cmd;
1093 todo = shell;
03f996bd 1094 }
1095
1096 /* --- A command given on the command line isn't too hard --- */
1097
1098 else if (optind < argc) {
c4f2d992 1099 todo = argv + optind;
03f996bd 1100 binary = todo[0];
1101 }
1102
45df69ab 1103 else {
1104 flags |= f_shell;
03f996bd 1105
45df69ab 1106 switch (style) {
c4f2d992 1107
45df69ab 1108 /* --- An unadorned becoming requires little work --- */
03f996bd 1109
45df69ab 1110 case l_preserve:
1111 shell[0] = getenv("SHELL");
1112 if (!shell[0])
1113 shell[0] = from_pw->pw_shell;
1114 shell[1] = 0;
1115 todo = shell;
1116 binary = todo[0];
1117 break;
387501ae 1118
45df69ab 1119 /* --- An su-like login needs slightly less effort --- */
1120
1121 case l_setuser:
1122 shell[0] = to_pw->pw_shell;
1123 shell[1] = 0;
1124 todo = shell;
1125 binary = todo[0];
1126 break;
1127
1128 /* --- A login request needs a little bit of work --- */
387501ae 1129
45df69ab 1130 case l_login: {
1131 const char *p = strrchr(to_pw->pw_shell, '/');
1132
1133 if (p)
1134 p++;
1135 else
1136 p = to_pw->pw_shell;
1137 shell[0] = xmalloc(strlen(p) + 2);
1138 shell[0][0] = '-';
1139 strcpy(shell[0] + 1, p);
1140 shell[1] = 0;
1141 todo = shell;
1142 binary = to_pw->pw_shell;
1143 } break;
1144 }
03f996bd 1145 }
1146
569b8891 1147 /* --- Mangle the environment --- *
1148 *
387501ae 1149 * This keeps getting more complicated all the time. (How true. Now I've
1150 * got all sorts of nasty environment mangling to do.)
1151 *
1152 * The environment stuff now happens in seven phases:
1153 *
1154 * 1. Mark very special variables to be preserved. Currently only TERM
1155 * and DISPLAY are treated in this way.
1156 *
1157 * 2. Set and preserve Become's own environment variables.
1158 *
1159 * 3. Set and preserve the user identity variables (USER, LOGNAME, HOME,
1160 * SHELL and MAIL) if we're being `su'-like or `login'-like.
1161 *
1162 * 4. If we're preserving the environment or being `su'-like, process the
1163 * PATH variable a little. Otherwise reset it to something
1164 * appropriate.
1165 *
1166 * 5. If we're being `login'-like, expunge all unpreserved variables.
1167 *
1168 * 6. Expunge any security-critical variables.
1169 *
1170 * 7. Build a new environment table to pass to child processes.
569b8891 1171 */
1172
1173 {
387501ae 1174 /* --- Variables to be preserved always --- *
1175 *
1176 * A user can explicitly expunge a variable in this list, in which case
1177 * we never get to see it here.
1178 */
569b8891 1179
387501ae 1180 static char *preserve[] = {
94455fbb 1181 "TERM", "DISPLAY", "TZ", 0
387501ae 1182 };
c6885d5f 1183
387501ae 1184 /* --- Variables to be expunged --- *
569b8891 1185 *
c6885d5f 1186 * Any environment string which has one of the following as a prefix will
1187 * be expunged from the environment passed to the called process. The
1188 * first line lists variables which have been used to list search paths
1189 * for shared libraries: by manipulating these, an attacker could replace
1190 * a standard library with one of his own. The second line lists other
1191 * well-known dangerous environment variables.
569b8891 1192 */
1193
387501ae 1194 static char *banned[] = {
1195 "-LD_", "SHLIB_PATH", "LIBPATH", "-_RLD_",
1196 "IFS", "ENV", "BASH_ENV", "KRB_CONF",
569b8891 1197 0
1198 };
1199
387501ae 1200 /* --- Other useful variables --- */
03f996bd 1201
387501ae 1202 sym_env *e;
1203 char *p, *q, *r;
1204 char **pp, **qq;
1205 size_t sz;
1206 unsigned f;
1207 sym_iter i;
c6885d5f 1208
387501ae 1209 /* --- Stage one. Preserve display-specific variables --- */
569b8891 1210
387501ae 1211 for (pp = preserve; *pp; pp++) {
1212 if ((e = sym_find(&bc__env, *pp, -1, 0, 0)) != 0)
1213 e->f |= envFlag_preserve;
1214 }
569b8891 1215
387501ae 1216 /* --- Stage two. Set Become's own variables --- */
569b8891 1217
387501ae 1218 e = sym_find(&bc__env, "BECOME_ORIGINAL_USER", -1, sizeof(*e), &f);
1219 if (!f)
1220 bc__setenv(e, from_pw->pw_name);
1221 e->f |= envFlag_preserve;
1222
1223 e = sym_find(&bc__env, "BECOME_ORIGINAL_HOME", -1, sizeof(*e), &f);
1224 if (!f)
1225 bc__setenv(e, from_pw->pw_dir);
1226 e->f |= envFlag_preserve;
569b8891 1227
387501ae 1228 bc__putenv("BECOME_OLD_USER", from_pw->pw_name, envFlag_preserve, 0);
1229 bc__putenv("BECOME_OLD_HOME", from_pw->pw_dir, envFlag_preserve, 0);
1230 bc__putenv("BECOME_USER", to_pw->pw_name, envFlag_preserve, 0);
1231 bc__putenv("BECOME_HOME", to_pw->pw_dir, envFlag_preserve, 0);
569b8891 1232
387501ae 1233 /* --- Stage three. Set user identity --- */
1234
1235 switch (style) {
1236 case l_login: {
1237 static char *maildirs[] = {
1238 "/var/spool/mail", "/var/mail",
1239 "/usr/spool/mail", "/usr/mail",
1240 0
1241 };
1242 struct stat s;
1243 char b[128];
1244
1245 for (pp = maildirs; *pp; pp++) {
1246 if (stat(*pp, &s) == 0 && S_ISDIR(s.st_mode)) {
1247 sprintf(b, "%s/%s", *pp, to_pw->pw_name);
1248 bc__putenv("MAIL", b, envFlag_preserve, 0);
1249 break;
569b8891 1250 }
569b8891 1251 }
387501ae 1252 } /* Fall through */
1253
a5089273 1254 case l_setuser:
387501ae 1255 bc__putenv("USER", to_pw->pw_name, envFlag_preserve, 0);
1256 bc__putenv("LOGNAME", to_pw->pw_name, envFlag_preserve, 0);
1257 bc__putenv("HOME", to_pw->pw_dir, envFlag_preserve, 0);
1258 bc__putenv("SHELL", to_pw->pw_shell, envFlag_preserve, 0);
1259 break;
1260 }
1261
1262 /* --- Stage four. Set the user's PATH properly --- */
569b8891 1263
387501ae 1264 {
1265 /* --- Find an existing path --- *
569b8891 1266 *
387501ae 1267 * If there's no path, or this is a login, then set a default path,
1268 * unless we're meant to preserve the existing one. Whew!
569b8891 1269 */
1270
387501ae 1271 e = sym_find(&bc__env, "PATH", -1, sizeof(*e), &f);
1272
1273 if (!f || (style == l_login && ~e->f & envFlag_preserve)) {
1274 bc__putenv("PATH",
1275 rq.to ? "/usr/bin:/bin" : "/usr/bin:/usr/sbin:/bin:/sbin",
1276 envFlag_preserve, 0);
1277 } else {
1278
1279 /* --- Find the string --- */
1280
1281 e->f = envFlag_preserve;
1282 p = strchr(e->val, '=') + 1;
1283 r = e->val;
1284
1285 /* --- Write the new version to a dynamically allocated buffer --- */
1286
1287 e->val = xmalloc(4 + 1 + strlen(p) + 1);
1288 strcpy(e->val, "PATH=");
1289 q = e->val + 5;
1290
1291 for (p = strtok(p, ":"); p; p = strtok(0, ":")) {
6a4ec25c 1292 if (p[0] != '/')
387501ae 1293 continue;
1294 while (*p)
1295 *q++ = *p++;
1296 *q++ = ':';
1297 }
1298 q[-1] = 0;
1299
1300 /* --- Done! --- */
1301
1302 free(r);
569b8891 1303 }
387501ae 1304 }
1305
1306 /* --- Stages five and six. Expunge variables and count numbers --- *
1307 *
1308 * Folded together, so I only need one pass through the table. Also
1309 * count the number of variables needed at this time.
1310 */
1311
1312 sz = 0;
569b8891 1313
387501ae 1314 for (sym_createIter(&i, &bc__env); (e = sym_next(&i)) != 0; ) {
569b8891 1315
387501ae 1316 /* --- Login style expunges all unpreserved variables --- */
569b8891 1317
387501ae 1318 if (style == l_login && ~e->f & envFlag_preserve)
1319 goto expunge;
1320
1321 /* --- Otherwise just check the name against the list --- */
1322
1323 for (pp = banned; *pp; pp++) {
1324 if (**pp == '-') {
1325 p = *pp + 1;
1326 if (memcmp(e->_base.name, p, strlen(p)) == 0)
1327 goto expunge;
a5089273 1328 } else if (strcmp(e->_base.name, *pp) == 0)
387501ae 1329 goto expunge;
1330 }
1331
1332 sz++;
1333 continue;
1334
1335 expunge:
1336 sym_remove(&bc__env, e);
569b8891 1337 }
387501ae 1338
1339 /* --- Stage seven. Build the new environment block --- */
1340
1341 env = qq = xmalloc((sz + 1) * sizeof(*qq));
1342
1343 for (sym_createIter(&i, &bc__env); (e = sym_next(&i)) != 0; )
1344 *qq++ = e->val;
1345 *qq++ = 0;
03f996bd 1346 }
1347
1348 /* --- Trace the command --- */
1349
1350 IF_TRACING(TRACE_SETUP, {
1351 int i;
1352
1353 trace(TRACE_SETUP, "setup: from user %s to user %s",
1354 from_pw->pw_name, to_pw->pw_name);
1355 trace(TRACE_SETUP, "setup: binary == `%s'", binary);
1356 for (i = 0; todo[i]; i++)
1357 trace(TRACE_SETUP, "setup: arg %i == `%s'", i, todo[i]);
1358 for (i = 0; env[i]; i++)
1359 trace(TRACE_SETUP, "setup: env %i == `%s'", i, env[i]);
1360 })
1361
c4f2d992 1362 /* --- If necessary, resolve the path to the command --- */
1363
03f996bd 1364 if (!strchr(binary, '/')) {
1365 char *path, *p;
c4f2d992 1366 struct stat st;
c4f2d992 1367
1368 if ((p = getenv("PATH")) == 0)
1369 p = "/bin:/usr/bin";
03f996bd 1370 path = xstrdup(p);
c4f2d992 1371
569b8891 1372 for (p = strtok(path, ":"); p; p = strtok(0, ":")) {
c4f2d992 1373
569b8891 1374 /* --- Check length of string before copying --- */
c4f2d992 1375
03f996bd 1376 if (strlen(p) + strlen(binary) + 2 > sizeof(rq.cmd))
c4f2d992 1377 continue;
1378
1379 /* --- Now build the pathname and check it --- */
1380
03f996bd 1381 sprintf(rq.cmd, "%s/%s", p, todo[0]);
1382 if (stat(rq.cmd, &st) == 0 && /* Check it exists */
c4f2d992 1383 st.st_mode & 0111 && /* Check it's executable */
c6885d5f 1384 S_ISREG(st.st_mode)) /* Check it's a file */
c4f2d992 1385 break;
1386 }
1387
1388 if (!p)
1389 die("couldn't find `%s' in path", todo[0]);
03f996bd 1390 binary = rq.cmd;
c4f2d992 1391 free(path);
1392 }
03f996bd 1393 T( trace(TRACE_SETUP, "setup: resolve binary to `%s'", binary); )
c4f2d992 1394
1395 /* --- Canonicalise the path string, if necessary --- */
1396
1397 {
1398 char b[CMDLEN_MAX];
1399 char *p;
1400 const char *q;
1401
1402 /* --- Insert current directory name if path not absolute --- */
1403
1404 p = b;
03f996bd 1405 q = binary;
c4f2d992 1406 if (*q != '/') {
1407 if (!getcwd(b, sizeof(b)))
1408 die("couldn't read current directory: %s", strerror(errno));
1409 p += strlen(p);
1410 *p++ = '/';
1411 }
1412
1413 /* --- Now copy over characters from the path string --- */
1414
1415 while (*q) {
1416
569b8891 1417 /* --- Check for buffer overflows here --- *
c4f2d992 1418 *
1419 * I write at most one byte per iteration so this is OK. Remember to
1420 * allow one for the null byte.
1421 */
1422
1423 if (p >= b + sizeof(b) - 1)
03f996bd 1424 die("internal error: buffer overflow while canonifying path");
c4f2d992 1425
1426 /* --- Reduce multiple slashes to just one --- */
1427
1428 if (*q == '/') {
1429 while (*q == '/')
1430 q++;
1431 *p++ = '/';
1432 }
1433
1434 /* --- Handle dots in filenames --- *
1435 *
1436 * @p[-1]@ is valid here, because if @*q@ is not a `/' then either
1437 * we've just stuck the current directory on the end of the buffer,
1438 * or we've just put something else on the end.
1439 */
1440
1441 else if (*q == '.' && p[-1] == '/') {
1442
1443 /* --- A simple `./' just gets removed --- */
1444
1445 if (q[1] == 0 || q[1] == '/') {
1446 q++;
1447 p--;
1448 continue;
1449 }
1450
1451 /* --- A `../' needs to be peeled back to the previous `/' --- */
1452
1453 if (q[1] == '.' && (q[2] == 0 || q[2] == '/')) {
1454 q += 2;
1455 p--;
1456 while (p > b && p[-1] != '/')
1457 p--;
1458 if (p > b)
1459 p--;
1460 continue;
1461 }
1462 } else
1463 *p++ = *q++;
1464 }
1465
1466 *p++ = 0;
1467 strcpy(rq.cmd, b);
1468 }
03f996bd 1469 T( trace(TRACE_SETUP, "setup: canonify binary to `%s'", rq.cmd); )
c4f2d992 1470
88e486d5 1471 /* --- Run the check --- *
1472 *
1473 * If the user is already what she wants to be, then print a warning.
1474 * Then, if I was just going to spawn a shell, quit, to reduce user
a5089273 1475 * confusion. Otherwise, do what was wanted anyway. Also, don't bother
1476 * checking if we're already root -- root can do anything anyway, and at
1477 * least this way we get some logging done, and offer a more friendly
1478 * front-end.
88e486d5 1479 */
c4f2d992 1480
88e486d5 1481 if (rq.from == rq.to) {
1482 moan("you already are `%s'!", to_pw->pw_name);
45df69ab 1483 if (flags & f_shell) {
88e486d5 1484 moan("(to prevent confusion, I'm not spawning a shell)");
1485 exit(0);
1486 }
1487 } else {
a5089273 1488 int a = (rq.from == 0) || check(&rq);
c4f2d992 1489
1490 syslog(LOG_INFO,
1491 "permission %s for %s to become %s to run `%s'",
03f996bd 1492 a ? "granted" : "denied", from_pw->pw_name, to_pw->pw_name,
1493 rq.cmd);
c4f2d992 1494
1495 if (!a)
1496 die("permission denied");
1497 }
1498
1499 /* --- Now do the job --- */
1500
03f996bd 1501 T( trace(TRACE_MISC, "become: permission granted"); )
1502
1503 if (flags & f_dummy) {
1504 puts("permission granted");
1505 return (0);
03f996bd 1506 }
a5089273 1507
1508#ifdef HAVE_SETGROUPS
1509 if (setgroups(ngroups, groups) < 0)
1510 die("couldn't set groups: %s", strerror(errno));
1511#endif
1512
1513 if (setgid(group) < 0)
1514 die("couldn't set default group: %s", strerror(errno));
1515 if (setuid(rq.to) < 0)
1516 die("couldn't set uid: %s", strerror(errno));
1517
45df69ab 1518 /* --- If this was a login, change current directory --- */
1519
f175e71b 1520 if ((flags & f_shell) &&
1521 style == l_login &&
1522 chdir(to_pw->pw_dir) < 0) {
45df69ab 1523 moan("couldn't change directory to `%s': %s",
1524 to_pw->pw_dir, strerror(errno));
1525 }
1526
a5089273 1527 /* --- Finally, call the program --- */
1528
1529 fflush(0);
46a5b3c6 1530 closelog();
a5089273 1531 execve(rq.cmd, todo, env);
1532 die("couldn't exec `%s': %s", rq.cmd, strerror(errno));
1533 return (127);
c4f2d992 1534}
1535
1536/*----- That's all, folks -------------------------------------------------*/