login_auth.c revision 22086
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1996 by
31556Srgrimes * Sean Eric Fagan <sef@kithrup.com>
41556Srgrimes * David Nugent <davidn@blaze.net.au>
51556Srgrimes * All rights reserved.
61556Srgrimes *
71556Srgrimes * Redistribution and use in source and binary forms, with or without
81556Srgrimes * modification, is permitted provided that the following conditions
91556Srgrimes * are met:
101556Srgrimes * 1. Redistributions of source code must retain the above copyright
111556Srgrimes *    notice immediately at the beginning of the file, without modification,
121556Srgrimes *    this list of conditions, and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 3. This work was done expressly for inclusion into FreeBSD.  Other use
171556Srgrimes *    is permitted provided this notation is included.
181556Srgrimes * 4. Absolutely no warranty of function or purpose is made by the authors.
191556Srgrimes * 5. Modifications may be freely made to this file providing the above
201556Srgrimes *    conditions are met.
211556Srgrimes *
221556Srgrimes * Low-level routines relating to the user capabilities database
231556Srgrimes *
241556Srgrimes *	$FreeBSD: head/lib/libutil/login_auth.c 22086 1997-01-29 06:11:31Z davidn $
251556Srgrimes */
261556Srgrimes
271556Srgrimes#include <sys/types.h>
281556Srgrimes#include <sys/time.h>
291556Srgrimes#include <sys/resource.h>
301556Srgrimes#include <sys/stat.h>
311556Srgrimes#include <errno.h>
321556Srgrimes#include <fcntl.h>
331556Srgrimes#include <limits.h>
341556Srgrimes#include <stdio.h>
351556Srgrimes#include <pwd.h>
361556Srgrimes#include <stdlib.h>
371556Srgrimes#include <string.h>
381556Srgrimes#include <syslog.h>
3935773Scharnier#include <unistd.h>
4036007Scharnier#include <login_cap.h>
4135773Scharnier#include <stdarg.h>
4235773Scharnier#include <paths.h>
4350471Speter#include <sys/wait.h>
441556Srgrimes
451556Srgrimesextern char *fgetline(FILE *, int*);
4636007Scharnier
471556Srgrimes#ifdef RLIM_LONG
481556Srgrimes# define STRTOV strtol
491556Srgrimes#else
5051137Sgreen# define STRTOV strtoq
511556Srgrimes#endif
521556Srgrimes
531556Srgrimes#define AUTHMAXLINES  1024
541556Srgrimes#define AUTHMAXARGS   16
551556Srgrimes
561556Srgrimesstruct auth_info {
571556Srgrimes  int reject;
581556Srgrimes  int auths;
591556Srgrimes  int env_count;
601556Srgrimes  char **env;
611556Srgrimes  int file_count;
621556Srgrimes  char **files;
631556Srgrimes};
641556Srgrimes
6551208Sgreenstatic struct auth_info auth_info;
6651208Sgreen
6748026Sgreen/*
6851208Sgreen * free_auth_info()
691556Srgrimes * Go through the auth_info structure, and free() anything of interest.
7051249Sgreen * This includes the string arrays, and any individual element.
7151249Sgreen * All part of being environmentally conscious ;).
7251137Sgreen */
7351208Sgreen
7451208Sgreenstatic void
751556Srgrimesfree_auth_info(void)
761556Srgrimes{
771556Srgrimes  int i;
781556Srgrimes
791556Srgrimes  auth_info.reject = 0;
801556Srgrimes  auth_info.auths = 0;
811556Srgrimes  if (auth_info.env) {
821556Srgrimes    for (i = 0; i < auth_info.env_count; i++) {
831556Srgrimes      if (auth_info.env[i])
841556Srgrimes	free(auth_info.env[i]);
851556Srgrimes    }
861556Srgrimes    free(auth_info.env);
871556Srgrimes    auth_info.env = NULL;
881556Srgrimes  }
891556Srgrimes  if (auth_info.files) {
901556Srgrimes    for (i = 0; i < auth_info.file_count; i++) {
911556Srgrimes      if (auth_info.files[i])
921556Srgrimes	free(auth_info.files[i]);
931556Srgrimes    }
941556Srgrimes    free(auth_info.files);
951556Srgrimes    auth_info.files = NULL;
961556Srgrimes  }
971556Srgrimes}
981556Srgrimes
991556Srgrimes
1001556Srgrimes/*
1011556Srgrimes * collect_info()
1021556Srgrimes * Read from <fd>, a list of authorization commands.
1031556Srgrimes * These commands are:
1041556Srgrimes *	reject
1051556Srgrimes *	authorize [root|secure]
1061556Srgrimes *	setenv <name>[ <value>]
1071556Srgrimes *	remove <file>
1081556Srgrimes * A single reject means the entire thing is bad;
1091556Srgrimes * multiple authorize statements can be present (it would be
1101556Srgrimes * silly, but that's what the spec says).
1111556Srgrimes * The commands are collected, and are accted upon by:
1121556Srgrimes *	auth_scan()	-- check for authorization or rejection
1131556Srgrimes *	auth_rmfiles()	-- remove the specified files
1141556Srgrimes *	auth_env()	-- set the specified environment variables
1151556Srgrimes * We only get up to AUTHMAXLINES lines of input from the program.
1161556Srgrimes */
1171556Srgrimes#define STRSIZEOF(x)  (sizeof(x)-1)
1181556Srgrimesstatic void
1191556Srgrimescollect_info(int fd)
1201556Srgrimes{
1211556Srgrimes  char *line;
1221556Srgrimes  FILE *fp;
1231556Srgrimes  char *ptr;
1241556Srgrimes  int len;
12548026Sgreen  int line_count = 0;
12648026Sgreen
1271556Srgrimes  fp = fdopen(fd, "r");
12851249Sgreen
12951249Sgreen  while ((line = fgetline(fp, &len)) != NULL) {
13051249Sgreen    if (++line_count > AUTHMAXLINES)
13151249Sgreen      break;
13251249Sgreen    if (strncasecmp(line, BI_REJECT, STRSIZEOF(BI_REJECT)) == 0) {
13362311Sgreen      auth_info.reject = 1;
13451137Sgreen    } else if (strncasecmp(line, BI_AUTH, STRSIZEOF(BI_AUTH)) == 0) {
13551335Sgreen      ptr = line + STRSIZEOF(BI_AUTH);
13651208Sgreen      ptr += strspn(ptr, " \t");
1371556Srgrimes      if (!*ptr)
1381556Srgrimes	auth_info.auths |= AUTH_OKAY;
1391556Srgrimes      else if (strncasecmp(ptr, BI_ROOTOKAY, STRSIZEOF(BI_ROOTOKAY)) == 0)
1401556Srgrimes	auth_info.auths |= AUTH_ROOTOKAY;
1411556Srgrimes      else if (strncasecmp(ptr, BI_SECURE, STRSIZEOF(BI_SECURE)) == 0)
1421556Srgrimes	auth_info.auths |= AUTH_SECURE;
1431556Srgrimes    } else if (strncasecmp(line, BI_SETENV, STRSIZEOF(BI_SETENV)) == 0) {
1441556Srgrimes      ptr = line + STRSIZEOF(BI_SETENV);
1451556Srgrimes      ptr += strspn(ptr, " \t");
14648026Sgreen      if (*ptr) {
1471556Srgrimes	char **tmp = realloc(auth_info.env, sizeof(char*) * (auth_info.env_count + 1));
1481556Srgrimes	if (tmp != NULL) {
1491556Srgrimes	  auth_info.env = tmp;
1501556Srgrimes	  if ((auth_info.env[auth_info.env_count] = strdup(ptr)) != NULL)
1511556Srgrimes	    auth_info.env_count++;
1521556Srgrimes	}
1531556Srgrimes      }
1541556Srgrimes    } else if (strncasecmp(line, BI_REMOVE, STRSIZEOF(BI_REMOVE)) == 0) {
1551556Srgrimes      ptr = line + STRSIZEOF(BI_REMOVE);
15648026Sgreen      ptr += strspn(ptr, " \t");
1571556Srgrimes      if (*ptr) {
1581556Srgrimes	char **tmp = realloc(auth_info.files, sizeof(char*) * (auth_info.file_count + 1));
1591556Srgrimes	if (tmp != NULL) {
1601556Srgrimes	  auth_info.files = tmp;
1611556Srgrimes	  if ((auth_info.files[auth_info.file_count] = strdup(ptr)) != NULL)
1621556Srgrimes	    auth_info.file_count++;
1631556Srgrimes	}
1641556Srgrimes      }
1651556Srgrimes    }
1661556Srgrimes  }
1671556Srgrimes  fclose(fp);
1681556Srgrimes}
16962311Sgreen
17062311Sgreen
17162311Sgreen/*
1721556Srgrimes * authenticate()
17362311Sgreen * Starts an auth_script() for the given <user>, with a class <class>,
17462311Sgreen * style <style>, and service <service>.  <style> is necessary,
17562311Sgreen * as are <user> and <class>, but <service> is optional -- it defaults
1761556Srgrimes * to "login".
1771556Srgrimes * Since auth_script() expects an execl'able program name, authenticate()
1781556Srgrimes * 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