1 /*
2 This program is free software; you can redistribute it and/or
3 modify it under the terms of the GNU General Public License as
4 published by the Free Software Foundation; either version 2, or (at
5 your option) any later version.
6
7 This program is distributed in the hope that it will be useful, but
8 WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 General Public License for more details.
11
12 Copyright (c) Alexey Mahotkin <alexm@hsys.msk.ru> 2002-2004
13
14 */
15
16 #include <config.h>
17
18 #include "logging.h"
19 #include "pam-support.h"
20
21 #include <errno.h>
22 #include <getopt.h>
23 #include <grp.h>
24 #include <pwd.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30
31 /* command line options processing */
32 int opt_debugging = 0;
33 static int opt_dont_set_env = 0;
34 static int opt_dont_chdir_home = 0;
35 int opt_use_stdout = 0;
36
37 static const char* short_options = "dehs:HV";
38
39 enum { OPT_STDOUT = 1 };
40 static struct option long_options[] = {
41 { "debug", no_argument, NULL, 'd' },
42 { "help", no_argument, NULL, 'h' },
43 { "noenv", no_argument, NULL, 'e' },
44 { "no-chdir-home", no_argument, NULL, 'H' },
45 { "service", required_argument, NULL, 's' },
46 { "stdout", no_argument, NULL, OPT_STDOUT },
47 { "version", no_argument, NULL, 'V' },
48 { NULL, 0, NULL, 0 }
49 };
50
51 static const char* usage =
52 "Usage: " PACKAGE " [OPTION]... -- prog...\n"
53 "\n"
54 "Authenticate using PAM and the checkpassword protocol:\n"
55 "\t<URL:http://cr.yp.to/checkpwd/interface.html>\n"
56 "and run the program specified as 'prog'\n"
57 "\n"
58 "Options are:\n"
59 " -d, --debug\t\tturn on debugging output\n"
60 " -e, --noenv\t\tdo not set uid, gid, environment variables, \n\t\t\tand home directory\n"
61 " -H, --no-chdir-home\tdo not change to home directory\n"
62 " -h, --help\t\tdisplay this help and exit\n"
63 " -s, --service=SERVICE\tspecify PAM service name to use\n"
64 "\t\t\t(by default use the contents of $PAM_SERVICE)\n"
65 " -V, --version\t\tdisplay version information and exit\n";
66
67
68 /* checkpassword protocol support */
69 #define PROTOCOL_FD 3
70 #define PROTOCOL_LEN 512
71 static char up[PROTOCOL_LEN];
72
73 /* pointers into up[] */
74 static char* username = NULL;
75 static char* password = NULL;
76
77 int
main(int argc,char * argv[])78 main (int argc, char *argv[])
79 {
80 FILE* protocol;
81 int i, uplen;
82 struct passwd* pw;
83 char* service_name = NULL;
84 int exit_status = 1;
85
86 init_logging(argv[0]);
87
88 /* process command line options */
89 opterr = 0;
90 while (1) {
91 int option_index = 0;
92 int c = getopt_long (argc, argv, short_options, long_options, &option_index);
93
94 if (c == -1)
95 break;
96
97 switch (c) {
98 case OPT_STDOUT:
99 opt_use_stdout = 1;
100 break;
101
102 case 'd':
103 opt_debugging = 1;
104 break;
105
106 case 'e':
107 opt_dont_set_env = 1;
108 break;
109
110 case 'H':
111 opt_dont_chdir_home = 1;
112 break;
113
114 case 'h':
115 puts(usage);
116 exit(0);
117
118 case 's':
119 service_name = strdup(optarg);
120 if (!service_name) {
121 fatal("Out of memory");
122 exit(1);
123 }
124 break;
125
126 case 'V':
127 puts(PACKAGE " " VERSION);
128 exit(0);
129
130 case '?':
131 fatal("Invalid command line, see --help");
132 exit(2);
133 }
134 }
135
136 if (service_name == NULL) {
137 char *envval = getenv("PAM_SERVICE");
138 if (!envval) {
139 fatal("PAM service name not specified");
140 exit_status = 2;
141 goto out;
142 }
143 service_name = strdup(envval);
144 if (!service_name) {
145
146 fatal("Out of memory");
147 exit_status = 111;
148 goto out;
149 }
150 }
151
152 terminate_logging();
153 init_logging(service_name);
154
155 /* read the username/password */
156 protocol = fdopen(PROTOCOL_FD, "r");
157 if (protocol == NULL) {
158 fatal("Error opening fd %d: %s", PROTOCOL_FD, strerror(errno));
159 exit_status = 2;
160 goto out;
161 }
162 debugging("Reading username and password");
163 uplen = fread(up, 1, PROTOCOL_LEN, protocol);
164 if (uplen == 0) {
165 fatal("Checkpassword protocol failure: zero bytes read");
166 exit_status = 2;
167 goto out;
168 }
169 i = 0;
170 /* extract username */
171 username = up + i;
172 while (up[i++]) {
173 if (i >= uplen) {
174 fatal("Checkpassword protocol failure: username not provided");
175 exit_status = 2;
176 goto out;
177 }
178 }
179 debugging("Username '%s'", username);
180
181 /* extract password */
182 password = up + i;
183 while (up[i++]) {
184 if (i >= uplen) {
185 fatal("Checkpassword protocol failure: password not provided");
186 exit_status = 2;
187 goto out;
188 }
189 }
190 debugging("Password read successfully");
191
192 /* authenticate using PAM */
193 exit_status = authenticate_using_pam(service_name, username, password);
194 if (exit_status != 0)
195 goto out;
196
197 if (opt_dont_set_env)
198 goto execute_program; /* skip setting up process environment */
199
200 /* switch to proper uid/gid/groups */
201 pw = getpwnam(username);
202 if (!pw) {
203 if (opt_debugging)
204 fatal("Error getting information about %s from /etc/passwd: %s", username, strerror(errno));
205 exit_status = 2;
206 goto out;
207 }
208
209 /* set supplementary groups */
210 if (initgroups(username, pw->pw_gid) == -1) {
211 fatal("Error setting supplementary groups for user %s: %s", username, strerror(errno));
212 exit_status = 111;
213 goto out;
214 }
215
216 /* set gid */
217 if (setgid(pw->pw_gid) == -1) {
218 fatal("setgid(%d) error: %s", pw->pw_gid, strerror(errno));
219 exit_status = 111;
220 goto out;
221 }
222
223 /* set uid */
224 if (setuid(pw->pw_uid) == -1) {
225 fatal("setuid(%d) error: %s", pw->pw_uid, strerror(errno));
226 exit_status = 111;
227 goto out;
228 }
229
230 if (!opt_dont_chdir_home) {
231 /* switch to user home directory */
232 if (chdir(pw->pw_dir) == -1) {
233 fatal("Error changing directory %s: %s", pw->pw_dir, strerror(errno));
234 exit_status = 1;
235 goto out;
236 }
237 }
238
239 /* set $USER */
240 if (setenv("USER", username, 1) == -1) {
241 fatal("Error setting $USER to %s: %s", username, strerror(errno));
242 exit_status = 111;
243 goto out;
244 }
245
246 /* set $HOME */
247 if (setenv("HOME", pw->pw_dir, 1) == -1) {
248 fatal("Error setting $HOME to %s: %s", pw->pw_dir, strerror(errno));
249 exit_status = 111;
250 goto out;
251 }
252
253 /* set $SHELL */
254 if (setenv("SHELL", pw->pw_shell, 1) == -1) {
255 fatal("Error setting $SHELL to %s: %s", pw->pw_shell, strerror(errno));
256 exit_status = 111;
257 goto out;
258 }
259
260 execute_program:
261
262 /* execute the program, if any */
263 if (optind < argc) {
264 debugging("Executing %s", argv[optind]);
265
266 execvp(argv[optind], argv + optind);
267 fatal("Cannot exec(%s): %s\n", argv[optind], strerror(errno));
268 exit_status = 2;
269 goto out;
270 }
271 /* if no program was provided in command line, simply exit */
272
273 out:
274 memset(up, 0x00, sizeof(up));
275
276 debugging("Exiting with status %d", exit_status);
277
278 terminate_logging();
279
280 exit(exit_status);
281 }
282