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