]> git.ipfire.org Git - thirdparty/util-linux.git/blob - login-utils/newgrp.c
Merge branch 'fix-minix' of https://github.com/rudimeier/util-linux
[thirdparty/util-linux.git] / login-utils / newgrp.c
1 /* setgrp.c - by Michael Haardt. Set the gid if possible
2 * Added a bit more error recovery/reporting - poe
3 * Vesa Roukonen added code for asking password */
4
5 /* 1999-02-22 Arkadiusz Miƛkiewicz <misiek@pld.ORG.PL>
6 * - added Native Language Support
7 */
8
9 #include <errno.h>
10 #include <getopt.h>
11 #include <grp.h>
12 #include <pwd.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <termios.h>
17 #include <unistd.h>
18
19 #ifdef HAVE_CRYPT_H
20 # include <crypt.h>
21 #endif
22
23 #ifdef HAVE_GETSGNAM
24 # include <gshadow.h>
25 #endif
26
27 #include "c.h"
28 #include "closestream.h"
29 #include "nls.h"
30 #include "pathnames.h"
31 #include "xalloc.h"
32
33 static char *xgetpass(FILE *input, const char *prompt)
34 {
35 char *pass = NULL;
36 struct termios saved, no_echo;
37 const int fd = fileno(input);
38 size_t dummy = 0;
39 ssize_t len;
40
41 fputs(prompt, stdout);
42 if (isatty(fd)) {
43 /* disable echo */
44 tcgetattr(fd, &saved);
45 no_echo = saved;
46 no_echo.c_lflag &= ~ECHO;
47 no_echo.c_lflag |= ECHONL;
48 if (tcsetattr(fd, TCSANOW, &no_echo))
49 err(EXIT_FAILURE, _("could not set terminal attributes"));
50 }
51 len = getline(&pass, &dummy, input);
52 if (isatty(fd))
53 /* restore terminal */
54 if (tcsetattr(fd, TCSANOW, &saved))
55 err(EXIT_FAILURE, _("could not set terminal attributes"));
56 if (len < 0)
57 err(EXIT_FAILURE, _("getline() failed"));
58 if (0 < len && *(pass + len - 1) == '\n')
59 *(pass + len - 1) = '\0';
60 return pass;
61 }
62
63 /* Ensure memory is set to value c without compiler optimization getting
64 * into way that could happen with memset(3). */
65 static int xmemset_s(void *v, size_t sz, const int c)
66 {
67 volatile unsigned char *p = v;
68
69 if (v == NULL)
70 return EINVAL;
71 while (sz--)
72 *p++ = c;
73 return 0;
74 }
75
76 /* try to read password from gshadow */
77 static char *get_gshadow_pwd(const char *groupname)
78 {
79 #ifdef HAVE_GETSGNAM
80 struct sgrp *sgrp;
81
82 sgrp = getsgnam(groupname);
83 return sgrp ? xstrdup(sgrp->sg_passwd) : NULL;
84 #else
85 char buf[BUFSIZ];
86 char *pwd = NULL;
87 FILE *f;
88
89 if (groupname == NULL || *groupname == '\0')
90 return NULL;
91
92 f = fopen(_PATH_GSHADOW, "r");
93 if (!f)
94 return NULL;
95
96 while (fgets(buf, sizeof buf, f)) {
97 char *cp = strchr(buf, ':');
98 if (!cp)
99 /* any junk in gshadow? */
100 continue;
101 *cp = '\0';
102 if (strcmp(buf, groupname) == 0) {
103 if (cp - buf >= BUFSIZ)
104 /* only group name on line */
105 break;
106 pwd = cp + 1;
107 if ((cp = strchr(pwd, ':')) && pwd == cp + 1)
108 /* empty password */
109 pwd = NULL;
110 else if (cp)
111 *cp = '\0';
112 break;
113 }
114 }
115 fclose(f);
116 return pwd ? xstrdup(pwd) : NULL;
117 #endif /* HAVE_GETSGNAM */
118 }
119
120 static int allow_setgid(const struct passwd *pe, const struct group *ge)
121 {
122 char **look;
123 int notfound = 1;
124 char *pwd, *xpwd;
125
126 if (getuid() == 0)
127 /* root may do anything */
128 return TRUE;
129 if (ge->gr_gid == pe->pw_gid)
130 /* You can switch back to your default group */
131 return TRUE;
132
133 look = ge->gr_mem;
134 while (*look && (notfound = strcmp(*look++, pe->pw_name))) ;
135
136 if (!notfound)
137 /* member of group => OK */
138 return TRUE;
139
140 /* Ask for password. Often there is no password in /etc/group, so
141 * contrary to login et al. we let an empty password mean the same
142 * as in /etc/passwd */
143
144 /* check /etc/gshadow */
145 if (!(pwd = get_gshadow_pwd(ge->gr_name)))
146 pwd = ge->gr_passwd;
147
148 if (pwd && *pwd && (xpwd = xgetpass(stdin, _("Password: ")))) {
149 char *cbuf = crypt(xpwd, pwd);
150
151 xmemset_s(xpwd, strlen(xpwd), 0);
152 free(xpwd);
153 if (!cbuf)
154 warn(_("crypt failed"));
155 else if (strcmp(pwd, cbuf) == 0)
156 return TRUE;
157 }
158
159 /* default to denial */
160 return FALSE;
161 }
162
163 static void __attribute__((__noreturn__)) usage(FILE *out)
164 {
165 fprintf(out, USAGE_HEADER);
166 fprintf(out, _(" %s <group>\n"), program_invocation_short_name);
167
168 fputs(USAGE_SEPARATOR, out);
169 fputs(_("Log in to a new group.\n"), out);
170
171 fprintf(out, USAGE_OPTIONS);
172 fprintf(out, USAGE_HELP);
173 fprintf(out, USAGE_VERSION);
174 fprintf(out, USAGE_MAN_TAIL("newgrp(1)"));
175 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
176 }
177
178 int main(int argc, char *argv[])
179 {
180 struct passwd *pw_entry;
181 struct group *gr_entry;
182 char *shell;
183 int ch;
184 static const struct option longopts[] = {
185 {"version", no_argument, NULL, 'V'},
186 {"help", no_argument, NULL, 'h'},
187 {NULL, 0, NULL, 0}
188 };
189
190 setlocale(LC_ALL, "");
191 bindtextdomain(PACKAGE, LOCALEDIR);
192 textdomain(PACKAGE);
193 atexit(close_stdout);
194
195 while ((ch = getopt_long(argc, argv, "Vh", longopts, NULL)) != -1)
196 switch (ch) {
197 case 'V':
198 printf(UTIL_LINUX_VERSION);
199 return EXIT_SUCCESS;
200 case 'h':
201 usage(stdout);
202 default:
203 usage(stderr);
204 }
205
206 if (!(pw_entry = getpwuid(getuid())))
207 err(EXIT_FAILURE, _("who are you?"));
208
209 if (argc < 2) {
210 if (setgid(pw_entry->pw_gid) < 0)
211 err(EXIT_FAILURE, _("setgid failed"));
212 } else {
213 errno = 0;
214 if (!(gr_entry = getgrnam(argv[1]))) {
215 if (errno)
216 err(EXIT_FAILURE, _("no such group"));
217 else
218 errx(EXIT_FAILURE, _("no such group"));
219 }
220 if (!allow_setgid(pw_entry, gr_entry))
221 errx(EXIT_FAILURE, _("permission denied"));
222 if (setgid(gr_entry->gr_gid) < 0)
223 err(EXIT_FAILURE, _("setgid failed"));
224 }
225
226 if (setuid(getuid()) < 0)
227 err(EXIT_FAILURE, _("setuid failed"));
228
229 fflush(NULL);
230 shell = (pw_entry->pw_shell && *pw_entry->pw_shell ?
231 pw_entry->pw_shell : _PATH_BSHELL);
232 execl(shell, shell, (char *)0);
233 warn(_("failed to execute %s"), shell);
234 fflush(stderr);
235
236 return EXIT_FAILURE;
237 }