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