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