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