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