login_cap.c revision 21399
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 *	$Id: login_cap.c,v 1.2 1997/01/07 13:29:21 davidn Exp $
25 */
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <errno.h>
31#include <unistd.h>
32
33#include <sys/types.h>
34#include <sys/time.h>
35#include <sys/resource.h>
36#include <sys/param.h>
37#include <pwd.h>
38#include <login_cap.h>
39
40#ifdef RLIM_LONG
41# define STRTOV strtol
42#else
43# define STRTOV strtoq
44#endif
45
46static int lc_object_count = 0;
47
48static size_t internal_stringsz = 0;
49static char * internal_string = NULL;
50static size_t internal_arraysz = 0;
51static char ** internal_array = NULL;
52
53static char *
54allocstr(char * str)
55{
56  char * p;
57  size_t sz = strlen(str) + 1;	/* realloc() only if necessary */
58  if (sz <= internal_stringsz)
59    p = internal_string;
60  else if ((p = realloc(internal_string, sz)) != NULL)  {
61    internal_stringsz = sz;
62    internal_string = strcpy(p, str);
63  }
64  return p;
65}
66
67static char **
68allocarray(size_t sz)
69{
70  char ** p;
71  if (sz <= internal_arraysz)
72    p = internal_array;
73  else if ((p = realloc(internal_array, sz * sizeof(char*))) != NULL) {
74    internal_arraysz = sz;
75    internal_array = p;
76  }
77  return p;
78}
79
80
81/*
82 * arrayize()
83 * Turn a simple string <str> seperated by any of
84 * the set of <chars> into an array.  The last element
85 * of the array will be NULL, as is proper.
86 * Free using freearraystr()
87 */
88
89static char **
90arrayize(char *str, const char *chars, int *size)
91{
92  int i;
93  char *ptr;
94  char **res = NULL;
95
96  for (i = 0, ptr = str; *ptr; i++) {
97    int count = strcspn(ptr, chars);
98    ptr = ptr + count + 1;
99  }
100
101  if ((ptr = allocstr(str)) == NULL) {
102    res = NULL;
103    i = 0;
104  } else if ((res = allocarray(++i)) == NULL) {
105    free(str);
106    i = 0;
107  } else {
108    for (i = 0; *ptr; i++) {
109      int count = strcspn(ptr, chars);
110      res[i] = ptr;
111      ptr += count;
112      if (*ptr)
113	*ptr++ = '\0';
114    }
115    res[i] = 0;
116  }
117  if (size)
118    *size = i;
119  return res;
120}
121
122static void
123freearraystr(char ** array)
124{
125  /*
126   * the array[0] should be free'd, and then array.
127   */
128  if (array) {
129    free(array[0]);
130    free(array);
131  }
132}
133
134
135/*
136 * login_close()
137 * Frees up all resources relating to a login class
138 *
139 */
140
141void
142login_close(login_cap_t * lc)
143{
144  if (lc) {
145    free(lc->lc_style);
146    free(lc->lc_class);
147    free(lc);
148    if (--lc_object_count == 0) {
149      free(internal_string);
150      free(internal_array);
151      internal_array = NULL;
152      internal_string = NULL;
153      cgetclose();
154    }
155  }
156}
157
158
159/*
160 * login_getclassbyname() get the login class by its name.
161 * If the name given is NULL or empty, the default class
162 * LOGIN_DEFCLASS (ie. "default") is fetched. If the
163 * 'dir' argument contains a non-NULL non-empty string,
164 * then the file _FILE_LOGIN_CONF is picked up from that
165 * directory instead of the system login database.
166 * Return a filled-out login_cap_t structure, including
167 * class name, and the capability record buffer.
168 */
169
170login_cap_t *
171login_getclassbyname(char const * name, char const * dir)
172{
173  login_cap_t *lc = malloc(sizeof(login_cap_t));
174
175  if (lc != NULL) {
176    int   i = 0;
177    char  userpath[MAXPATHLEN];
178    static char *login_dbarray[] = { NULL, NULL, NULL };
179
180    if (dir && snprintf(userpath, MAXPATHLEN, "%s/%s", dir, _FILE_LOGIN_CONF) < MAXPATHLEN)
181      login_dbarray[i++] = userpath;
182    else
183      login_dbarray[i++]   = _PATH_LOGIN_CONF;
184    login_dbarray[i  ]   = NULL;
185
186    lc->lc_cap = lc->lc_class = lc->lc_style = NULL;
187
188    if ((name == NULL || cgetent(&lc->lc_cap, login_dbarray, (char*)name) != 0) &&
189	cgetent(&lc->lc_cap, login_dbarray, (char*)(name = LOGIN_DEFCLASS)) != 0) {
190	free(lc);
191	lc = NULL;
192    } else {
193      ++lc_object_count;
194      lc->lc_class = strdup(name);
195    }
196  }
197
198  return lc;
199}
200
201
202
203/*
204 * login_getclass()
205 * Get the login class for a given password entry from
206 * the system (only) login class database.
207 * If the password entry's class field is not set, or
208 * the class specified does not exist, then use the
209 * default of LOGIN_DEFCLASS (ie. "default").
210 * Return a filled-out login_cap_t structure, including
211 * class name, and the capability record buffer.
212 */
213
214login_cap_t *
215login_getclass(const struct passwd *pwd)
216{
217  const char * class = NULL;
218  if (pwd == NULL) {
219    if ((class = pwd->pw_class) == NULL || *class == '\0')
220      class = (pwd->pw_uid == 0) ? "root" : NULL;
221  }
222  return login_getclassbyname(class, 0);
223}
224
225
226/*
227 * login_getuserclass()
228 * Get the login class for a given password entry, allowing user
229 * overrides via ~/.login_conf.
230 * ### WAS: If the password entry's class field is not set,
231 * #######  or the class specified does not exist, then use
232 * If an entry with the recordid "me" does not exist, then use
233 * the default of LOGIN_DEFCLASS (ie. "default").
234 * Return a filled-out login_cap_t structure, including
235 * class name, and the capability record buffer.
236 */
237
238login_cap_t *
239login_getuserclass(const struct passwd *pwd)
240{
241  const char * class = "me"; /* (pwd == NULL) ? NULL : pwd->pw_class; */
242  const char * home  = (pwd == NULL) ? NULL : pwd->pw_dir;
243  return login_getclassbyname(class, home);
244}
245
246
247
248/*
249 * login_getcapstr()
250 * Given a login_cap entry, and a capability name, return the
251 * value defined for that capability, a defualt if not found, or
252 * an error string on error.
253 */
254
255char *
256login_getcapstr(login_cap_t *lc, const char *cap, char *def, char *error)
257{
258  char *res;
259  int ret;
260
261  if (lc == NULL || cap == NULL || lc->lc_cap == NULL || *cap == '\0')
262    return def;
263
264  if ((ret = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) {
265    return def;
266  } else if (ret >= 0)
267    return res;
268  else
269    return error;
270}
271
272
273/*
274 * login_getcaplist()
275 * Given a login_cap entry, and a capability name, return the
276 * value defined for that capability split into an array of
277 * strings.
278 */
279
280char **
281login_getcaplist(login_cap_t *lc, const char * cap, const char * chars)
282{
283  char * lstring;
284
285  if (chars == NULL)
286    chars = ". \t";
287  if ((lstring = login_getcapstr(lc, (char*)cap, NULL, NULL)) != NULL)
288    return arrayize(lstring, chars, NULL);
289  return NULL;
290}
291
292
293/*
294 * login_getpath()
295 * From the login_cap_t <lc>, get the capability <cap> which is
296 * formatted as either a space or comma delimited list of paths
297 * and append them all into a string and separate by semicolons.
298 * If there is an error of any kind, return <error>.
299 */
300
301char *
302login_getpath(login_cap_t *lc, const char *cap, char * error)
303{
304  char *ptr, *str = login_getcapstr(lc, (char*)cap, NULL, NULL);
305
306  if (str == NULL || (ptr = allocstr(str)) == NULL)
307    str = error;
308  else {
309    while (*ptr) {
310      int count = strcspn(ptr, ", \t");
311      ptr += count;
312      if (*ptr)
313	*ptr++ = ':';
314    }
315  }
316  return str;
317}
318
319
320/*
321 * login_getcaptime()
322 * From the login_cap_t <lc>, get the capability <cap>, which is
323 * formatted as a time (e.g., "<cap>=10h3m2s").  If <cap> is not
324 * present in <lc>, return <def>; if there is an error of some kind,
325 * return <error>.
326 */
327
328rlim_t
329login_getcaptime(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error)
330{
331  char *res, *ep;
332  int ret;
333  rlim_t tot = 0, tim;
334
335  errno = 0;
336  if (lc == NULL || lc->lc_cap == NULL)
337    return def;
338
339  /*
340   * Look for <cap> in lc_cap.
341   * If it's not there (-1), return <def>.
342   * If there's an error, return <error>.
343   */
344
345  if ((ret = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1)
346    return def;
347  else if (ret < 0)
348    return error;
349
350  /*
351   * "inf" and "infinity" are two special cases for this.
352   */
353  if (!strcasecmp(res, "infinity") || !strcasecmp(res, "inf"))
354    return RLIM_INFINITY;
355
356  /*
357   * Now go through the string, turning something like 1h2m3s into
358   * an integral value.  Whee.
359   */
360
361  errno = 0;
362  while (*res) {
363    tim = STRTOV(res, &ep, 0);
364    if ((ep == NULL) || (ep == res) || errno) {
365      return error;
366    }
367    /* Look for suffixes */
368    switch (*ep++) {
369    case 0:
370      ep--; break;	/* end of string */
371    case 's': case 'S':	/* seconds */
372      break;
373    case 'm': case 'M':	/* minutes */
374      tim *= 60L;
375      break;
376    case 'h': case 'H':	/* hours */
377      tim *= (60L * 60L);
378      break;
379    case 'd': case 'D':	/* days */
380      tim *= (60L * 60L * 24L);
381      break;
382    case 'w': case 'W':	/* weeks */
383      tim *= (60L * 60L * 24L * 7L);
384    case 'y': case 'Y':	/* Years */
385      /* I refuse to take leap years into account here.  Sue me. */
386      tim *= (60L * 60L * 24L * 365L);
387    default:
388      return error;
389    }
390    res = ep;
391    tot += tim;
392  }
393  return tot;
394}
395
396
397/*
398 * login_getcapnum()
399 * From the login_cap_t <lc>, extract the numerical value <cap>.
400 * If it is not present, return <def> for a default, and return
401 * <error> if there is an error.
402 * Like login_getcaptime(), only it only converts to a number, not
403 * to a time; "infinity" and "inf" are 'special.'
404 */
405
406rlim_t
407login_getcapnum(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error)
408{
409  char *ep, *res;
410  int ret;
411  rlim_t val;
412
413  if (lc == NULL || lc->lc_cap == NULL)
414    return def;
415
416  /*
417   * For BSDI compatibility, try for the tag=<val> first
418   */
419  if ((ret = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) {
420    long lval;
421    /*
422     * String capability not present, so try for tag#<val> as numeric
423     */
424    if ((ret = cgetnum(lc->lc_cap, (char *)cap, &lval)) == -1)
425      return def; /* Not there, so return default */
426    else if (ret < 0)
427      return error;
428    return (rlim_t)lval;
429  }
430  else if (ret < 0)
431    return error;
432
433  if (!strcasecmp(res, "infinity") || !strcasecmp(res, "inf"))
434    return RLIM_INFINITY;
435
436  errno = 0;
437  val = STRTOV(res, &ep, 0);
438  if ((ep == NULL) || (ep == res) || errno)
439    return error;
440  return val;
441}
442
443
444/*
445 * login_getcapsize()
446 * From the login_cap_t <lc>, extract the capability <cap>, which is
447 * formatted as a size (e.g., "<cap>=10M"); it can also be "infinity".
448 * If not present, return <def>, or <error> if there is an error of
449 * some sort.
450 */
451
452rlim_t
453login_getcapsize(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) {
454  char *ep, *res;
455  int ret;
456  rlim_t val;
457  rlim_t mult;
458
459  if (lc == NULL || lc->lc_cap == NULL)
460    return def;
461
462  if ((ret = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1)
463    return def;
464  else if (ret < 0)
465    return error;
466
467  errno = 0;
468  val = STRTOV(res, &ep, 0);
469  if ((res == NULL) || (res == ep) || errno)
470    return error;
471  switch (*ep) {
472  case 0:	/* end of string */
473    mult = 1; break;
474  case 'b': case 'B':	/* 512-byte blocks */
475    mult = 512; break;
476  case 'k': case 'K':	/* 1024-byte Kilobytes */
477    mult = 1024; break;
478  case 'm': case 'M':	/* 1024-k kbytes */
479    mult = 1024 * 1024; break;
480  case 'g': case 'G':	/* 1Gbyte */
481    mult = 1024 * 1024 * 1024; break;
482#ifndef RLIM_LONG
483  case 't': case 'T':	/* 1TBte */
484    mult = 1024LL * 1024LL * 1024LL * 1024LL; break;
485#endif
486  default:
487    return error;
488  }
489  return val * mult;
490}
491
492
493/*
494 * login_getcapbool()
495 * From the login_cap_t <lc>, check for the existance of the capability
496 * of <cap>.  Return <def> if <lc>->lc_cap is NULL, otherwise return
497 * the whether or not <cap> exists there.
498 */
499
500int
501login_getcapbool(login_cap_t *lc, const char *cap, int def)
502{
503  if (lc == NULL || lc->lc_cap == NULL)
504    return def;
505  return (cgetcap(lc->lc_cap, (char *)cap, ':') != NULL);
506}
507
508
509/*
510 * login_getstyle()
511 * Given a login_cap entry <lc>, and optionally a type of auth <auth>,
512 * and optionally a style <style>, find the style that best suits these
513 * rules:
514 *	1.  If <auth> is non-null, look for an "auth-<auth>=" string
515 *	in the capability; if not present, default to "auth=".
516 *	2.  If there is no auth list found from (1), default to
517 *	"passwd" as an authorization list.
518 *	3.  If <style> is non-null, look for <style> in the list of
519 *	authorization methods found from (2); if <style> is NULL, default
520 *	to LOGIN_DEFSTYLE ("passwd").
521 *	4.  If the chosen style is found in the chosen list of authorization
522 *	methods, return that; otherwise, return NULL.
523 * E.g.:
524 *     login_getstyle(lc, NULL, "ftp");
525 *     login_getstyle(lc, "login", NULL);
526 *     login_getstyle(lc, "skey", "network");
527 */
528
529char *
530login_getstyle(login_cap_t *lc, char *style, const char *auth)
531{
532  int  i;
533  char **authtypes = NULL;
534  char *auths= NULL;
535  char realauth[64];
536
537  static char *defauthtypes[] = { LOGIN_DEFSTYLE, NULL };
538
539  if (auth != NULL && *auth != '\0' &&
540      snprintf(realauth, sizeof realauth, "auth-%s", auth) < sizeof realauth)
541    authtypes = login_getcaplist(lc, realauth, NULL);
542
543  if (authtypes == NULL)
544    authtypes = login_getcaplist(lc, "auth", NULL);
545
546  if (authtypes == NULL)
547    authtypes = defauthtypes;
548
549  /*
550   * We have at least one authtype now; auths is a comma-seperated
551   * (or space-separated) list of authentication types.  We have to
552   * convert from this to an array of char*'s; authtypes then gets this.
553   */
554  i = 0;
555  if (style != NULL && *style != '\0') {
556    while (authtypes[i] != NULL && strcmp(style, authtypes[i]) != 0)
557      i++;
558  }
559  lc->lc_style = NULL;
560  if (authtypes[i] != NULL && (auths = strdup(authtypes[i])) != NULL)
561    lc->lc_style = auths;
562
563  return lc->lc_style;
564}
565
566
567