xref: /freebsd/lib/libutil/login_auth.c (revision e9039c38dc0e9cb9b255c4cd77c68e44e1067188)
1 /*-
2  * Copyright (c) 1996 by
3  * Sean Eric Fagan <sef@kithrup.com>
4  * David Nugent <davidn@blaze.net.au>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, is permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice immediately at the beginning of the file, without modification,
12  *    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. This work was done expressly for inclusion into FreeBSD.  Other use
17  *    is permitted provided this notation is included.
18  * 4. Absolutely no warranty of function or purpose is made by the authors.
19  * 5. Modifications may be freely made to this file providing the above
20  *    conditions are met.
21  *
22  * Low-level routines relating to the user capabilities database
23  *
24  *	$FreeBSD$
25  */
26 
27 #include <sys/types.h>
28 #include <sys/time.h>
29 #include <sys/resource.h>
30 #include <sys/stat.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <limits.h>
34 #include <stdio.h>
35 #include <pwd.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <syslog.h>
39 #include <unistd.h>
40 #include <login_cap.h>
41 #include <stdarg.h>
42 #include <paths.h>
43 #include <sys/wait.h>
44 
45 #ifdef RLIM_LONG
46 # define STRTOV strtol
47 #else
48 # define STRTOV strtoq
49 #endif
50 
51 #define AUTHMAXLINES  1024
52 #define AUTHMAXARGS   16
53 
54 struct auth_info {
55   int reject;
56   int auths;
57   int env_count;
58   char **env;
59   int file_count;
60   char **files;
61 };
62 
63 static struct auth_info auth_info;
64 
65 /*
66  * free_auth_info()
67  * Go through the auth_info structure, and free() anything of interest.
68  * This includes the string arrays, and any individual element.
69  * All part of being environmentally conscious ;).
70  */
71 
72 static void
73 free_auth_info(void)
74 {
75   int i;
76 
77   auth_info.reject = 0;
78   auth_info.auths = 0;
79   if (auth_info.env) {
80     for (i = 0; i < auth_info.env_count; i++) {
81       if (auth_info.env[i])
82 	free(auth_info.env[i]);
83     }
84     free(auth_info.env);
85     auth_info.env = NULL;
86   }
87   if (auth_info.files) {
88     for (i = 0; i < auth_info.file_count; i++) {
89       if (auth_info.files[i])
90 	free(auth_info.files[i]);
91     }
92     free(auth_info.files);
93     auth_info.files = NULL;
94   }
95 }
96 
97 
98 /*
99  * collect_info()
100  * Read from <fd>, a list of authorization commands.
101  * These commands are:
102  *	reject
103  *	authorize [root|secure]
104  *	setenv <name>[ <value>]
105  *	remove <file>
106  * A single reject means the entire thing is bad;
107  * multiple authorize statements can be present (it would be
108  * silly, but that's what the spec says).
109  * The commands are collected, and are accted upon by:
110  *	auth_scan()	-- check for authorization or rejection
111  *	auth_rmfiles()	-- remove the specified files
112  *	auth_env()	-- set the specified environment variables
113  * We only get up to AUTHMAXLINES lines of input from the program.
114  */
115 #define STRSIZEOF(x)  (sizeof(x)-1)
116 static void
117 collect_info(int fd)
118 {
119   char *line;
120   FILE *fp;
121   char *ptr;
122   size_t len;
123   int line_count = 0;
124 
125   fp = fdopen(fd, "r");
126 
127   while ((line = fgetln(fp, &len)) != NULL) {
128     if (++line_count > AUTHMAXLINES)
129       break;
130     if (len && line[len-1] == '\n')
131       --len;
132     line[len] = '\0'; /* Terminate */
133     if (strncasecmp(line, BI_REJECT, STRSIZEOF(BI_REJECT)) == 0) {
134       auth_info.reject = 1;
135     } else if (strncasecmp(line, BI_AUTH, STRSIZEOF(BI_AUTH)) == 0) {
136       ptr = line + STRSIZEOF(BI_AUTH);
137       ptr += strspn(ptr, " \t");
138       if (!*ptr)
139 	auth_info.auths |= AUTH_OKAY;
140       else if (strncasecmp(ptr, BI_ROOTOKAY, STRSIZEOF(BI_ROOTOKAY)) == 0)
141 	auth_info.auths |= AUTH_ROOTOKAY;
142       else if (strncasecmp(ptr, BI_SECURE, STRSIZEOF(BI_SECURE)) == 0)
143 	auth_info.auths |= AUTH_SECURE;
144     } else if (strncasecmp(line, BI_SETENV, STRSIZEOF(BI_SETENV)) == 0) {
145       ptr = line + STRSIZEOF(BI_SETENV);
146       ptr += strspn(ptr, " \t");
147       if (*ptr) {
148 	char **tmp = realloc(auth_info.env, sizeof(char*) * (auth_info.env_count + 1));
149 	if (tmp != NULL) {
150 	  auth_info.env = tmp;
151 	  if ((auth_info.env[auth_info.env_count] = strdup(ptr)) != NULL)
152 	    auth_info.env_count++;
153 	}
154       }
155     } else if (strncasecmp(line, BI_REMOVE, STRSIZEOF(BI_REMOVE)) == 0) {
156       ptr = line + STRSIZEOF(BI_REMOVE);
157       ptr += strspn(ptr, " \t");
158       if (*ptr) {
159 	char **tmp = realloc(auth_info.files, sizeof(char*) * (auth_info.file_count + 1));
160 	if (tmp != NULL) {
161 	  auth_info.files = tmp;
162 	  if ((auth_info.files[auth_info.file_count] = strdup(ptr)) != NULL)
163 	    auth_info.file_count++;
164 	}
165       }
166     }
167   }
168   fclose(fp);
169 }
170 
171 
172 /*
173  * authenticate()
174  * Starts an auth_script() for the given <user>, with a class <class>,
175  * style <style>, and service <service>.  <style> is necessary,
176  * as are <user> and <class>, but <service> is optional -- it defaults
177  * to "login".
178  * Since auth_script() expects an execl'able program name, authenticate()
179  * also concatenates <style> to _PATH_AUTHPROG.
180  * Lastly, calls auth_scan(AUTH_NONE) to see if there are any "reject" statements,
181  * or lack of "auth" statements.
182  * Returns -1 on error, 0 on rejection, and >0 on success.
183  * (See AUTH_* for the return values.)
184  *
185  */
186 int
187 authenticate(const char * name, const char * class, const char * style, const char *service)
188 {
189   int retval;
190 
191   if (style == NULL || *style == '\0')
192     retval = -1;
193   else {
194     char buf[sizeof(_PATH_AUTHPROG) + 64];
195 
196     if (service == NULL || *service == '\0')
197       service = LOGIN_DEFSERVICE;
198 
199     free_auth_info();
200 
201     if (snprintf(buf, sizeof buf, _PATH_AUTHPROG "%s", style) >= sizeof buf)
202       retval = -1;
203     else {
204       retval = auth_script(buf, style, "-s", service, name, class, NULL);
205       if (retval >= 0)
206 	retval = auth_scan(AUTH_NONE);
207     }
208   }
209   return retval;
210 }
211 
212 
213 /*
214  * auth_script()
215  * Runs an authentication program with specified arguments.
216  * It sets up file descriptor 3 for the program to write to;
217  * it stashes the output somewhere.  The output of the program
218  * consists of statements:
219  *	reject
220  *	authorize [root|secure]
221  *	setenv <name> [<value>]
222  *	remove <file>
223  *
224  * Terribly exciting, isn't it?  There is no limit specified in
225  * BSDi's API for how much output can be present, but we should
226  * keep it fairly small, I think.
227  * No more than AUTHMAXLINES lines.
228  */
229 
230 int
231 auth_script(const char * path, ...)
232 {
233   va_list ap;
234   int pid, status;
235   int argc = 0;
236   int p[2];	/* pipes */
237   char *argv[AUTHMAXARGS+1];
238 
239   va_start(ap, path);
240   while (argc < AUTHMAXARGS && (argv[argc++] = va_arg(ap, char*)) != NULL)
241     ;
242   argv[argc] = NULL;
243   va_end(ap);
244 
245   fflush(NULL);
246 
247   if (pipe(p) >= 0) {
248     if ((pid = fork()) == -1) {
249       close(p[0]);
250       close(p[1]);
251     } else if (pid == 0) {    /* Child */
252       close(p[0]);
253       dup2(p[1], 3);
254       if (setenv("PATH", _PATH_DEFPATH, 1)==0 && setenv("SHELL", _PATH_BSHELL, 1)==0)
255 	execv(path, argv);
256       _exit(1);
257     } else {
258       close(p[1]);
259       collect_info(p[0]);
260       if (waitpid(pid, &status, 0) != -1 && WIFEXITED(status) && !WEXITSTATUS(status))
261 	return 0;
262     }
263   }
264   return -1;
265 }
266 
267 
268 /*
269  * auth_env()
270  * Processes the stored "setenv" lines from the stored authentication
271  * output.
272  */
273 
274 int
275 auth_env(void)
276 {
277   int i;
278 
279   for (i = 0; i < auth_info.env_count; i++) {
280     char *nam = auth_info.env[i];
281     char *ptr = nam + strcspn(nam, " \t=");
282     if (*ptr) {
283       *ptr++ = '\0';
284       ptr += strspn(ptr, " \t");
285     }
286     setenv(nam, ptr, 1);
287   }
288   return 0;
289 }
290 
291 
292 /*
293  * auth_scan()
294  * Goes through the output of the auth_script/authenticate, and
295  * checks for a failure or authentication.
296  * <ok> is a default authentication value -- if there are no
297  * rejection or authentication statements, then it is returned
298  * unmodified.
299  * AUTH_NONE is returned if there were any reject statements
300  * from the authentication program (invoked by auth_script()), and
301  * AUTH, AUTH_ROOTOKAY, and/or AUTH_SECURE are returned if the
302  * appropriate directives were found.  Note that AUTH* are
303  * *bitmasks*!
304  */
305 
306 int
307 auth_scan(int ok)
308 {
309   if (auth_info.reject)
310     return 0;
311   return ok | auth_info.auths;
312 }
313 
314 
315 /*
316  * auth_rmfiles()
317  * Removes any files that the authentication program said needed to be
318  * removed, said files having come from a previous execution of
319  * auth_script().
320  */
321 
322 int
323 auth_rmfiles(void)
324 {
325   int i = auth_info.file_count;
326   while (i-- > 0) {
327     unlink(auth_info.files[i]);
328     free(auth_info.files[i]);
329     auth_info.files[i] = NULL;
330   }
331   return 0;
332 }
333 
334 
335 /*
336  * auth_checknologin()
337  * Checks for the existance of a nologin file in the login_cap
338  * capability <lc>.  If there isn't one specified, then it checks
339  * to see if this class should just ignore nologin files.  Lastly,
340  * it tries to print out the default nologin file, and, if such
341  * exists, it exits.
342  */
343 
344 void
345 auth_checknologin(login_cap_t *lc)
346 {
347   char *file;
348 
349   /* Do we ignore a nologin file? */
350   if (login_getcapbool(lc, "ignorenologin", 0))
351     return;
352 
353   /* Note that <file> will be "" if there is no nologin capability */
354   if ((file = login_getcapstr(lc, "nologin", "", NULL)) == NULL)
355     exit(1);
356 
357   /*
358    * *file is true IFF there was a "nologin" capability
359    * Note that auth_cat() returns 1 only if the specified
360    * file exists, and is readable.  E.g., /.nologin exists.
361    */
362   if ((*file && auth_cat(file)) || auth_cat(_PATH_NOLOGIN))
363     exit(1);
364 }
365 
366 
367 /*
368  * auth_cat()
369  * Checks for the readability of <file>; if it can be opened for
370  * reading, it prints it out to stdout, and then exits.  Otherwise,
371  * it returns 0 (meaning no nologin file).
372  */
373 int
374 auth_cat(const char *file)
375 {
376   int fd, count;
377   char buf[BUFSIZ];
378 
379   if ((fd = open(file, O_RDONLY)) < 0)
380     return 0;
381   while ((count = read(fd, buf, sizeof(buf))) > 0)
382     write(fileno(stdout), buf, count);
383   close(fd);
384   return 1;
385 }
386