]> granicus.if.org Git - shadow/blob - src/newgrp.c
c96544cecec372545cdcdba3574e1d217943b194
[shadow] / src / newgrp.c
1 /*
2  * Copyright (c) 1990 - 1994, Julianne Frances Haugh
3  * Copyright (c) 1996 - 2000, Marek Michałkiewicz
4  * Copyright (c) 2001 - 2006, Tomasz Kłoczko
5  * Copyright (c) 2007 - 2008, Nicolas François
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the copyright holders or contributors may not be used to
17  *    endorse or promote products derived from this software without
18  *    specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <config.h>
34
35 #ident "$Id$"
36
37 #include <errno.h>
38 #include <grp.h>
39 #include <pwd.h>
40 #include <stdio.h>
41 #include "defines.h"
42 #include "getdef.h"
43 #include "prototypes.h"
44 #include "exitcodes.h"
45 /*
46  * Global variables
47  */
48 extern char **newenvp;
49 extern char **environ;
50
51 #ifdef HAVE_SETGROUPS
52 static int ngroups;
53 static GETGROUPS_T *grouplist;
54 #endif
55
56 static char *Prog;
57 static bool is_newgrp;
58
59 #ifdef WITH_AUDIT
60 char audit_buf[80];
61 #endif
62
63 /* local function prototypes */
64 static void usage (void);
65 static void check_perms (const struct group *grp,
66                          struct passwd *pwd,
67                          const char *groupname);
68 static void syslog_sg (const char *name, const char *group);
69
70 /*
71  * usage - print command usage message
72  */
73 static void usage (void)
74 {
75         if (is_newgrp) {
76                 fputs (_("Usage: newgrp [-] [group]\n"), stderr);
77         } else {
78                 fputs (_("Usage: sg group [[-c] command]\n"), stderr);
79         }
80 }
81
82 /*
83  * find_matching_group - search all groups of a given group id for
84  *                       membership of a given username
85  */
86 static struct group *find_matching_group (const char *name, gid_t gid)
87 {
88         struct group *gr;
89         char **look;
90         bool notfound = true;
91
92         setgrent ();
93         while ((gr = getgrent ()) != NULL) {
94                 if (gr->gr_gid != gid) {
95                         continue;
96                 }
97
98                 /*
99                  * A group with matching GID was found.
100                  * Test for membership of 'name'.
101                  */
102                 look = gr->gr_mem;
103                 while ((NULL != *look) && notfound) {
104                         notfound = (strcmp (*look, name) != 0);
105                         look++;
106                 }
107                 if (!notfound) {
108                         break;
109                 }
110         }
111         endgrent ();
112         return gr;
113 }
114
115 /*
116  * check_perms - check if the user is allowed to switch to this group
117  *
118  *      If needed, the user will be authenticated.
119  *
120  *      It will not return if the user could not be authenticated.
121  */
122 static void check_perms (const struct group *grp,
123                          struct passwd *pwd,
124                          const char *groupname)
125 {
126         bool needspasswd = false;
127         struct spwd *spwd;
128         char *cp;
129         const char *cpasswd;
130
131         /*
132          * see if she is a member of this group (i.e. in the list of
133          * members of the group, or if the group is her primary group).
134          *
135          * If she isn't a member, she needs to provide the group password.
136          * If there is no group password, she will be denied access
137          * anyway.
138          *
139          */
140         if (   (grp->gr_gid != pwd->pw_gid)
141             && !is_on_list (grp->gr_mem, pwd->pw_name)) {
142                 needspasswd = true;
143         }
144
145         /*
146          * If she does not have either a shadowed password, or a regular
147          * password, and the group has a password, she needs to give the
148          * group password.
149          */
150         spwd = xgetspnam (pwd->pw_name);
151         if (NULL != spwd) {
152                 pwd->pw_passwd = spwd->sp_pwdp;
153         }
154
155         if ((pwd->pw_passwd[0] == '\0') && (grp->gr_passwd[0] != '\0')) {
156                 needspasswd = true;
157         }
158
159         /*
160          * Now I see about letting her into the group she requested. If she
161          * is the root user, I'll let her in without having to prompt for
162          * the password. Otherwise I ask for a password if she flunked one
163          * of the tests above.
164          */
165         if ((getuid () != 0) && needspasswd) {
166                 /*
167                  * get the password from her, and set the salt for
168                  * the decryption from the group file.
169                  */
170                 cp = getpass (_("Password: "));
171                 if (NULL == cp) {
172                         goto failure;
173                 }
174
175                 /*
176                  * encrypt the key she gave us using the salt from the
177                  * password in the group file. The result of this encryption
178                  * must match the previously encrypted value in the file.
179                  */
180                 cpasswd = pw_encrypt (cp, grp->gr_passwd);
181                 strzero (cp);
182
183                 if (grp->gr_passwd[0] == '\0' ||
184                     strcmp (cpasswd, grp->gr_passwd) != 0) {
185 #ifdef WITH_AUDIT
186                         snprintf (audit_buf, sizeof(audit_buf),
187                                   "authentication new-gid=%d", grp->gr_gid);
188                         audit_logger (AUDIT_GRP_AUTH, Prog,
189                                       audit_buf, NULL, getuid (), 0);
190 #endif
191                         SYSLOG ((LOG_INFO,
192                                  "Invalid password for group `%s' from `%s'",
193                                  groupname, pwd->pw_name));
194                         sleep (1);
195                         fputs (_("Invalid password.\n"), stderr);
196                         goto failure;
197                 }
198 #ifdef WITH_AUDIT
199                 snprintf (audit_buf, sizeof(audit_buf),
200                           "authentication new-gid=%d", grp->gr_gid);
201                 audit_logger (AUDIT_GRP_AUTH, Prog,
202                               audit_buf, NULL, getuid (), 1);
203 #endif
204         }
205
206         return;
207
208 failure:
209         /* The closelog is probably unnecessary, but it does no
210          * harm.  -- JWP
211          */
212         closelog ();
213 #ifdef WITH_AUDIT
214         if (groupname) {
215                 snprintf (audit_buf, sizeof(audit_buf),
216                           "changing new-group=%s", groupname);
217                 audit_logger (AUDIT_CHGRP_ID, Prog,
218                               audit_buf, NULL, getuid (), 0);
219         } else {
220                 audit_logger (AUDIT_CHGRP_ID, Prog, "changing",
221                               NULL, getuid (), 0);
222         }
223 #endif
224         exit (1);
225 }
226
227 #ifdef USE_SYSLOG
228 /*
229  * syslog_sg - log the change of group to syslog
230  *
231  *      The loggout will also be logged when the user will quit the
232  *      sg/newgrp session.
233  */
234 static void syslog_sg (const char *name, const char *group)
235 {
236         const char *loginname = getlogin ();
237         const char *tty = ttyname (0);
238
239         if (loginname != NULL) {
240                 loginname = xstrdup (loginname);
241         }
242         if (tty != NULL) {
243                 tty = xstrdup (tty);
244         }
245
246         if (loginname == NULL) {
247                 loginname = "???";
248         }
249         if (tty == NULL) {
250                 tty = "???";
251         } else if (strncmp (tty, "/dev/", 5) == 0) {
252                 tty += 5;
253         }
254         SYSLOG ((LOG_INFO,
255                  "user `%s' (login `%s' on %s) switched to group `%s'",
256                  name, loginname, tty, group));
257 #ifdef USE_PAM
258         /*
259          * We want to fork and exec the new shell in the child, leaving the
260          * parent waiting to log the session close.
261          *
262          * The parent must ignore signals generated from the console
263          * (SIGINT, SIGQUIT, SIGHUP) which might make the parent terminate
264          * before its child. When bash is exec'ed as the subshell, it
265          * generates a new process group id for itself, and consequently
266          * only SIGHUP, which is sent to all process groups in the session,
267          * can reach the parent. However, since arbitrary programs can be
268          * specified as login shells, there is no such guarantee in general.
269          * For the same reason, we must also ignore stop signals generated
270          * from the console (SIGTSTP, SIGTTIN, and SIGTTOU) in order to
271          * avoid any possibility of the parent being stopped when it
272          * receives SIGCHLD from the terminating subshell.  -- JWP
273          */
274         {
275                 pid_t child, pid;
276
277                 signal (SIGINT, SIG_IGN);
278                 signal (SIGQUIT, SIG_IGN);
279                 signal (SIGHUP, SIG_IGN);
280                 signal (SIGTSTP, SIG_IGN);
281                 signal (SIGTTIN, SIG_IGN);
282                 signal (SIGTTOU, SIG_IGN);
283                 child = fork ();
284                 if (child < 0) {
285                         /* error in fork() */
286                         fprintf (stderr, _("%s: failure forking: %s\n"),
287                                  is_newgrp ? "newgrp" : "sg", strerror (errno));
288 #ifdef WITH_AUDIT
289                         if (group) {
290                                 snprintf (audit_buf, sizeof(audit_buf),
291                                           "changing new-group=%s", group);
292                                 audit_logger (AUDIT_CHGRP_ID, Prog,
293                                               audit_buf, NULL, getuid (), 0);
294                         } else {
295                                 audit_logger (AUDIT_CHGRP_ID, Prog, "changing",
296                                               NULL, getuid (), 0);
297                         }
298 #endif
299                         exit (1);
300                 } else if (child != 0) {
301                         /* parent - wait for child to finish, then log session close */
302                         int cst = 0;
303                         gid_t gid = getgid();
304                         struct group *grp = getgrgid (gid);
305
306                         do {
307                                 errno = 0;
308                                 pid = waitpid (child, &cst, WUNTRACED);
309                                 if ((pid == child) && (WIFSTOPPED (cst) != 0)) {
310                                         /* stop when child stops */
311                                         kill (getpid (), WSTOPSIG(cst));
312                                         /* wake child when resumed */
313                                         kill (child, SIGCONT);
314                                 }
315                         } while (   ((pid == child) && (WIFSTOPPED (cst) != 0))
316                                  || ((pid != child) && (errno == EINTR)));
317                         /* local, no need for xgetgrgid */
318                         if (NULL != grp) {
319                                 SYSLOG ((LOG_INFO,
320                                          "user `%s' (login `%s' on %s) returned to group `%s'",
321                                          name, loginname, tty, grp->gr_name));
322                         } else {
323                                 SYSLOG ((LOG_INFO,
324                                          "user `%s' (login `%s' on %s) returned to group `%d'",
325                                          name, loginname, tty, gid));
326                                 /* Either the user's passwd entry has a
327                                  * GID that does not match with any group,
328                                  * or the group was deleted while the user
329                                  * was in a newgrp session.*/
330                                 SYSLOG ((LOG_WARN,
331                                          "unknown GID `%u' used by user `%s'",
332                                          gid, name));
333                         }
334                         closelog ();
335                         exit (0);
336                 }
337
338                 /* child - restore signals to their default state */
339                 signal (SIGINT, SIG_DFL);
340                 signal (SIGQUIT, SIG_DFL);
341                 signal (SIGHUP, SIG_DFL);
342                 signal (SIGTSTP, SIG_DFL);
343                 signal (SIGTTIN, SIG_DFL);
344                 signal (SIGTTOU, SIG_DFL);
345         }
346 #endif                          /* USE_PAM */
347 }
348 #endif                          /* USE_SYSLOG */
349
350 /*
351  * newgrp - change the invokers current real and effective group id
352  */
353 int main (int argc, char **argv)
354 {
355         bool initflag = false;
356         int i;
357         bool cflag = false;
358         int err = 0;
359         gid_t gid;
360         char *cp;
361         const char *name, *prog;
362         char *group = NULL;
363         char *command = NULL;
364         char **envp = environ;
365         struct passwd *pwd;
366         struct group *grp;
367
368 #ifdef SHADOWGRP
369         struct sgrp *sgrp;
370 #endif
371
372 #ifdef WITH_AUDIT
373         audit_help_open ();
374 #endif
375         (void) setlocale (LC_ALL, "");
376         (void) bindtextdomain (PACKAGE, LOCALEDIR);
377         (void) textdomain (PACKAGE);
378
379         /*
380          * Save my name for error messages and save my real gid incase of
381          * errors. If there is an error i have to exec a new login shell for
382          * the user since her old shell won't have fork'd to create the
383          * process. Skip over the program name to the next command line
384          * argument.
385          *
386          * This historical comment, and the code itself, suggest that the
387          * behavior of the system/shell on which it was written differed
388          * significantly from the one I am using. If this process was
389          * started from a shell (including the login shell), it was fork'ed
390          * and exec'ed as a child by that shell. In order to get the user
391          * back to that shell, it is only necessary to exit from this
392          * process which terminates the child of the fork. The parent shell,
393          * which is blocked waiting for a signal, will then receive a
394          * SIGCHLD and will continue; any changes made to the process
395          * persona or the environment after the fork never occurred in the
396          * parent process.
397          *
398          * Bottom line: we want to save the name and real gid for messages,
399          * but we do not need to restore the previous process persona and we
400          * don't need to re-exec anything.  -- JWP
401          */
402         Prog = Basename (argv[0]);
403         is_newgrp = (strcmp (Prog, "newgrp") == 0);
404         OPENLOG (is_newgrp ? "newgrp" : "sg");
405         gid = getgid ();
406         argc--;
407         argv++;
408
409         initenv ();
410
411         pwd = get_my_pwent ();
412         if (NULL == pwd) {
413                 fprintf (stderr, _("unknown UID: %u\n"), getuid ());
414 #ifdef WITH_AUDIT
415                 audit_logger (AUDIT_CHGRP_ID, Prog, "changing", NULL,
416                               getuid (), 0);
417 #endif
418                 SYSLOG ((LOG_WARN, "unknown UID %u", getuid ()));
419                 closelog ();
420                 exit (1);
421         }
422         name = pwd->pw_name;
423
424         /*
425          * Parse the command line. There are two accepted flags. The first
426          * is "-", which for newgrp means to re-create the entire
427          * environment as though a login had been performed, and "-c", which
428          * for sg causes a command string to be executed.
429          *
430          * The next argument, if present, must be the new group name. Any
431          * remaining remaining arguments will be used to execute a command
432          * as the named group. If the group name isn't present, I just use
433          * the login group ID of the current user.
434          *
435          * The valid syntax are
436          *      newgrp [-] [groupid]
437          *      newgrp [-l] [groupid]
438          *      sg [-]
439          *      sg [-] groupid [[-c command]
440          */
441         if (   (argc > 0)
442             && (   (strcmp (argv[0], "-")  == 0)
443                 || (strcmp (argv[0], "-l") == 0))) {
444                 argc--;
445                 argv++;
446                 initflag = true;
447         }
448         if (!is_newgrp) {
449                 /* 
450                  * Do the command line for everything that is
451                  * not "newgrp".
452                  */
453                 if ((argc > 0) && (argv[0][0] != '-')) {
454                         group = argv[0];
455                         argc--;
456                         argv++;
457                 } else {
458                         usage ();
459                         closelog ();
460                         exit (1);
461                 }
462                 if (argc > 0) {
463
464                         /*
465                          * skip -c if specified so both forms work:
466                          * "sg group -c command" (as in the man page) or
467                          * "sg group command" (as in the usage message).
468                          */
469                         if ((argc > 1) && (strcmp (argv[0], "-c") == 0)) {
470                                 command = argv[1];
471                         } else {
472                                 command = argv[0];
473                         }
474                         cflag = true;
475                 }
476         } else {
477                 /*
478                  * Do the command line for "newgrp". It's just making sure
479                  * there aren't any flags and getting the new group name.
480                  */
481                 if ((argc > 0) && (argv[0][0] == '-')) {
482                         usage ();
483                         goto failure;
484                 } else if (argv[0] != (char *) 0) {
485                         group = argv[0];
486                 } else {
487                         /*
488                          * get the group file entry for her login group id. 
489                          * the entry must exist, simply to be annoying.
490                          *
491                          * Perhaps in the past, but the default behavior now depends on the
492                          * group entry, so it had better exist.  -- JWP
493                          */
494                         grp = xgetgrgid (pwd->pw_gid);
495                         if (NULL == grp) {
496                                 fprintf (stderr, _("unknown GID: %lu\n"),
497                                          (unsigned long) pwd->pw_gid);
498                                 SYSLOG ((LOG_CRIT, "unknown GID: %lu",
499                                          (unsigned long) pwd->pw_gid));
500                                 goto failure;
501                         } else {
502                                 group = grp->gr_name;
503                         }
504                 }
505         }
506
507 #ifdef HAVE_SETGROUPS
508         /*
509          * get the current users groupset. The new group will be added to
510          * the concurrent groupset if there is room, otherwise you get a
511          * nasty message but at least your real and effective group id's are
512          * set.
513          */
514         /* don't use getgroups(0, 0) - it doesn't work on some systems */
515         i = 16;
516         for (;;) {
517                 grouplist = (GETGROUPS_T *) xmalloc (i * sizeof (GETGROUPS_T));
518                 ngroups = getgroups (i, grouplist);
519                 if (i > ngroups && !(ngroups == -1 && errno == EINVAL)) {
520                         break;
521                 }
522                 /* not enough room, so try allocating a larger buffer */
523                 free (grouplist);
524                 i *= 2;
525         }
526         if (ngroups < 0) {
527                 perror ("getgroups");
528 #ifdef WITH_AUDIT
529                 if (group) {
530                         snprintf (audit_buf, sizeof(audit_buf),
531                                   "changing new-group=%s", group);
532                         audit_logger (AUDIT_CHGRP_ID, Prog,
533                                       audit_buf, NULL, getuid (), 0);
534                 } else {
535                         audit_logger (AUDIT_CHGRP_ID, Prog,
536                                       "changing", NULL, getuid (), 0);
537                 }
538 #endif
539                 exit (1);
540         }
541 #endif                          /* HAVE_SETGROUPS */
542
543         /*
544          * now we put her in the new group. The password file entry for her
545          * current user id has been gotten. If there was no optional group
546          * argument she will have her real and effective group id set to the
547          * set to the value from her password file entry.  
548          *
549          * If run as newgrp, or as sg with no command, this process exec's
550          * an interactive subshell with the effective GID of the new group. 
551          * If run as sg with a command, that command is exec'ed in this
552          * subshell. When this process terminates, either because the user
553          * exits, or the command completes, the parent of this process
554          * resumes with the current GID.
555          *
556          * If a group is explicitly specified on the command line, the
557          * interactive shell or command is run with that effective GID. 
558          * Access will be denied if no entry for that group can be found in
559          * /etc/group. If the current user name appears in the members list
560          * for that group, access will be granted immediately; if not, the
561          * user will be challenged for that group's password. If the
562          * password response is incorrect, if the specified group does not
563          * have a password, or if that group has been locked by gpasswd -R,
564          * access will be denied. This is true even if the group specified
565          * has the user's login GID (as shown in /etc/passwd). If no group
566          * is explicitly specified on the command line, the effect is
567          * exactly the same as if a group name matching the user's login GID
568          * had been explicitly specified. Root, however, is never
569          * challenged for passwords, and is always allowed access.
570          *
571          * The previous behavior was to allow access to the login group if
572          * no explicit group was specified, irrespective of the group
573          * control file(s). This behavior is usually not desirable. A user
574          * wishing to return to the login group has only to exit back to the
575          * login shell. Generating yet more shell levels in order to
576          * provide a convenient "return" to the default group has the
577          * undesirable side effects of confusing the user, scrambling the
578          * history file, and consuming system resources. The default now is
579          * to lock out such behavior. A sys admin can allow it by explicitly
580          * including the user's name in the member list of the user's login
581          * group.  -- JWP
582          */
583         grp = getgrnam (group); /* local, no need for xgetgrnam */
584         if (NULL == grp) {
585                 fprintf (stderr, _("unknown group: %s\n"), group);
586                 goto failure;
587         }
588
589         /*
590          * For splitted groups (due to limitations of NIS), check all 
591          * groups of the same GID like the requested group for
592          * membership of the current user.
593          */
594         grp = find_matching_group (name, grp->gr_gid);
595         if (NULL == grp) {
596                 /*
597                  * No matching group found. As we already know that
598                  * the group exists, this happens only in the case
599                  * of a requested group where the user is not member.
600                  *
601                  * Re-read the group entry for further processing.
602                  */
603                 grp = xgetgrnam (group);
604         }
605 #ifdef SHADOWGRP
606         sgrp = getsgnam (group);
607         if (NULL != sgrp) {
608                 grp->gr_passwd = sgrp->sg_passwd;
609                 grp->gr_mem = sgrp->sg_mem;
610         }
611 #endif
612
613         /*
614          * Check if the user is allowed to access this group.
615          */
616         check_perms (grp, pwd, group);
617
618         /*
619          * all successful validations pass through this point. The group id
620          * will be set, and the group added to the concurrent groupset.
621          */
622 #ifdef  USE_SYSLOG
623         if (getdef_bool ("SYSLOG_SG_ENAB")) {
624                 syslog_sg (name, group);
625         }
626 #endif                          /* USE_SYSLOG */
627
628         gid = grp->gr_gid;
629
630 #ifdef HAVE_SETGROUPS
631         /*
632          * I am going to try to add her new group id to her concurrent group
633          * set. If the group id is already present i'll just skip this part. 
634          * If the group doesn't fit, i'll complain loudly and skip this
635          * part.
636          */
637         for (i = 0; i < ngroups; i++) {
638                 if (gid == grouplist[i]) {
639                         break;
640                 }
641         }
642         if (i == ngroups) {
643                 if (ngroups >= sysconf (_SC_NGROUPS_MAX)) {
644                         fputs (_("too many groups\n"), stderr);
645                 } else {
646                         grouplist[ngroups++] = gid;
647                         if (setgroups (ngroups, grouplist)) {
648                                 perror ("setgroups");
649                         }
650                 }
651         }
652 #endif
653
654         /*
655          * Set the effective GID to the new group id and the effective UID
656          * to the real UID. For root, this also sets the real GID to the
657          * new group id.
658          */
659         if (setgid (gid) != 0) {
660                 perror ("setgid");
661 #ifdef WITH_AUDIT
662                 snprintf (audit_buf, sizeof(audit_buf),
663                           "changing new-gid=%d", gid);
664                 audit_logger (AUDIT_CHGRP_ID, Prog,
665                               audit_buf, NULL, getuid (), 0);
666 #endif
667                 exit (1);
668         }
669
670         if (setuid (getuid ()) != 0) {
671                 perror ("setuid");
672 #ifdef WITH_AUDIT
673                 snprintf (audit_buf, sizeof(audit_buf),
674                           "changing new-gid=%d", gid);
675                 audit_logger (AUDIT_CHGRP_ID, Prog,
676                               audit_buf, NULL, getuid (), 0);
677 #endif
678                 exit (1);
679         }
680
681         /*
682          * See if the "-c" flag was used. If it was, i just create a shell
683          * command for her using the argument that followed the "-c" flag.
684          */
685         if (cflag) {
686                 closelog ();
687                 execl ("/bin/sh", "sh", "-c", command, (char *) 0);
688 #ifdef WITH_AUDIT
689                 snprintf (audit_buf, sizeof(audit_buf),
690                           "changing new-gid=%d", gid);
691                 audit_logger (AUDIT_CHGRP_ID, Prog,
692                               audit_buf, NULL, getuid (), 0);
693 #endif
694                 perror ("/bin/sh");
695                 exit (errno == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
696         }
697
698         /*
699          * I have to get the pathname of her login shell. As a favor, i'll
700          * try her environment for a $SHELL value first, and then try the
701          * password file entry. Obviously this shouldn't be in the
702          * restricted command directory since it could be used to leave the
703          * restricted environment.
704          *
705          * Note that the following assumes this user's entry in /etc/passwd
706          * does not have a chroot * prefix. If it does, the * will be copied
707          * verbatim into the exec path. This is probably not an issue
708          * because if this user is operating in a chroot jail, her entry in
709          * the version of /etc/passwd that is accessible here should
710          * probably never have a chroot shell entry (but entries for other
711          * users might). If I have missed something, and this causes you a
712          * problem, try using $SHELL as a workaround; also please notify me
713          * at jparmele@wildbear.com -- JWP
714          */
715         cp = getenv ("SHELL");
716         if (!initflag && (NULL != cp)) {
717                 prog = cp;
718         } else if ((NULL != pwd->pw_shell) && ('\0' != pwd->pw_shell[0])) {
719                 prog = pwd->pw_shell;
720         } else {
721                 prog = "/bin/sh";
722         }
723
724         /*
725          * Now I try to find the basename of the login shell. This will
726          * become argv[0] of the spawned command.
727          */
728         cp = Basename ((char *) prog);
729
730         endspent ();
731 #ifdef  SHADOWGRP
732         endsgent ();
733 #endif
734         endpwent ();
735         endgrent ();
736
737         /*
738          * Switch back to her home directory if i am doing login
739          * initialization.
740          */
741         if (initflag) {
742                 if (chdir (pwd->pw_dir) != 0) {
743                         perror ("chdir");
744                 }
745
746                 while (NULL != *envp) {
747                         if (strncmp (*envp, "PATH=", 5) == 0 ||
748                             strncmp (*envp, "HOME=", 5) == 0 ||
749                             strncmp (*envp, "SHELL=", 6) == 0 ||
750                             strncmp (*envp, "TERM=", 5) == 0)
751                                 addenv (*envp, NULL);
752
753                         envp++;
754                 }
755         } else {
756                 while (NULL != *envp) {
757                         addenv (*envp, NULL);
758                         envp++;
759                 }
760         }
761
762 #ifdef WITH_AUDIT
763         snprintf (audit_buf, sizeof(audit_buf), "changing new-gid=%d", gid);
764         audit_logger (AUDIT_CHGRP_ID, Prog, audit_buf, NULL, getuid (), 1);
765 #endif
766         /*
767          * Exec the login shell and go away. We are trying to get back to
768          * the previous environment which should be the user's login shell.
769          */
770         err = shell (prog, initflag ? (char *) 0 : cp, newenvp);
771         exit (err == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
772         /* NOTREACHED */
773       failure:
774
775         /*
776          * The previous code, when run as newgrp, re-exec'ed the shell in
777          * the current process with the original gid on error conditions. 
778          * See the comment above. This historical behavior now has the
779          * effect of creating unlogged extraneous shell layers when the
780          * command line has an error or there is an authentication failure.
781          * We now just want to exit with error status back to the parent
782          * process. The closelog is probably unnecessary, but it does no
783          * harm.  -- JWP
784          */
785         closelog ();
786 #ifdef WITH_AUDIT
787         if (NULL != group) {
788                 snprintf (audit_buf, sizeof(audit_buf),
789                           "changing new-group=%s", group);
790                 audit_logger (AUDIT_CHGRP_ID, Prog, 
791                               audit_buf, NULL, getuid (), 0);
792         } else {
793                 audit_logger (AUDIT_CHGRP_ID, Prog,
794                               "changing", NULL, getuid (), 0);
795         }
796 #endif
797         exit (1);
798 }
799