login_cap.c revision 83516
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 * Portions copyright (c) 1995,1997
8 * Berkeley Software Design, Inc.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, is permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice immediately at the beginning of the file, without modification,
16 *    this list of conditions, and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 * 3. This work was done expressly for inclusion into FreeBSD.  Other use
21 *    is permitted provided this notation is included.
22 * 4. Absolutely no warranty of function or purpose is made by the authors.
23 * 5. Modifications may be freely made to this file providing the above
24 *    conditions are met.
25 *
26 * Low-level routines relating to the user capabilities database
27 *
28 * $FreeBSD: head/lib/libutil/login_cap.c 83516 2001-09-15 16:12:56Z rwatson $
29 */
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <errno.h>
35#include <fcntl.h>
36#include <unistd.h>
37
38#include <sys/types.h>
39#include <sys/time.h>
40#include <sys/resource.h>
41#include <sys/param.h>
42#include <pwd.h>
43#include <libutil.h>
44#include <syslog.h>
45#include <login_cap.h>
46
47/*
48 * allocstr()
49 * Manage a single static pointer for handling a local char* buffer,
50 * resizing as necessary to contain the string.
51 *
52 * allocarray()
53 * Manage a static array for handling a group of strings, resizing
54 * when necessary.
55 */
56
57static int lc_object_count = 0;
58
59static size_t internal_stringsz = 0;
60static char * internal_string = NULL;
61static size_t internal_arraysz = 0;
62static char ** internal_array = NULL;
63
64static char *
65allocstr(char *str)
66{
67    char    *p;
68
69    size_t sz = strlen(str) + 1;	/* realloc() only if necessary */
70    if (sz <= internal_stringsz)
71	p = strcpy(internal_string, str);
72    else if ((p = realloc(internal_string, sz)) != NULL) {
73	internal_stringsz = sz;
74	internal_string = strcpy(p, str);
75    }
76    return p;
77}
78
79
80static char **
81allocarray(size_t sz)
82{
83    char    **p;
84
85    if (sz <= internal_arraysz)
86	p = internal_array;
87    else if ((p = realloc(internal_array, sz * sizeof(char*))) != NULL) {
88	internal_arraysz = sz;
89	internal_array = p;
90    }
91    return p;
92}
93
94
95/*
96 * arrayize()
97 * Turn a simple string <str> separated by any of
98 * the set of <chars> into an array.  The last element
99 * of the array will be NULL, as is proper.
100 * Free using freearraystr()
101 */
102
103static char **
104arrayize(char *str, const char *chars, int *size)
105{
106    int	    i;
107    char    *ptr;
108    char    **res = NULL;
109
110    /* count the sub-strings */
111    for (i = 0, ptr = str; *ptr; i++) {
112	int count = strcspn(ptr, chars);
113	ptr += count;
114	if (*ptr)
115	    ++ptr;
116    }
117
118    /* alloc the array */
119    if ((ptr = allocstr(str)) != NULL) {
120	if ((res = allocarray(++i)) == NULL)
121	    free(str);
122	else {
123	    /* now split the string */
124	    i = 0;
125	    while (*ptr) {
126		int count = strcspn(ptr, chars);
127		res[i++] = ptr;
128		ptr += count;
129		if (*ptr)
130		    *ptr++ = '\0';
131	    }
132	    res[i] = NULL;
133	}
134    }
135
136    if (size)
137	*size = i;
138
139    return res;
140}
141
142
143/*
144 * login_close()
145 * Frees up all resources relating to a login class
146 *
147 */
148
149void
150login_close(login_cap_t * lc)
151{
152    if (lc) {
153	free(lc->lc_style);
154	free(lc->lc_class);
155	free(lc->lc_cap);
156	free(lc);
157	if (--lc_object_count == 0) {
158	    free(internal_string);
159	    free(internal_array);
160	    internal_array = NULL;
161	    internal_arraysz = 0;
162	    internal_string = NULL;
163	    internal_stringsz = 0;
164	    cgetclose();
165	}
166    }
167}
168
169
170/*
171 * login_getclassbyname() get the login class by its name.
172 * If the name given is NULL or empty, the default class
173 * LOGIN_DEFCLASS (ie. "default") is fetched. If the
174 * 'dir' argument contains a non-NULL non-empty string,
175 * then the file _FILE_LOGIN_CONF is picked up from that
176 * directory instead of the system login database.
177 * Return a filled-out login_cap_t structure, including
178 * class name, and the capability record buffer.
179 */
180
181login_cap_t *
182login_getclassbyname(char const *name, const struct passwd *pwd)
183{
184    login_cap_t	*lc;
185
186    if ((lc = malloc(sizeof(login_cap_t))) != NULL) {
187	int	    r, i = 0;
188	uid_t euid = 0;
189	gid_t egid = 0;
190	const char  *msg = NULL;
191	const char  *dir = (pwd == NULL) ? NULL : pwd->pw_dir;
192	char	    userpath[MAXPATHLEN];
193
194	static char *login_dbarray[] = { NULL, NULL, NULL };
195
196	/*
197	 * Switch to user mode before checking/reading its ~/.login_conf
198	 * - some NFSes have root read access disabled.
199	 *
200	 * XXX: This fails to configure additional groups.
201	 */
202	if (dir) {
203	    euid = geteuid();
204	    egid = getegid();
205	    (void)setegid(pwd->pw_gid);
206	    (void)seteuid(pwd->pw_uid);
207	}
208
209	if (dir && snprintf(userpath, MAXPATHLEN, "%s/%s", dir,
210			    _FILE_LOGIN_CONF) < MAXPATHLEN) {
211	    login_dbarray[i] = userpath;
212	    if (_secure_path(userpath, pwd->pw_uid, pwd->pw_gid) != -1)
213		i++;		/* only use 'secure' data */
214	}
215	if (_secure_path(_PATH_LOGIN_CONF, 0, 0) != -1)
216	    login_dbarray[i++] = _PATH_LOGIN_CONF;
217	login_dbarray[i] = NULL;
218
219	memset(lc, 0, sizeof(login_cap_t));
220	lc->lc_cap = lc->lc_class = lc->lc_style = NULL;
221
222	if (name == NULL || *name == '\0')
223	    name = LOGIN_DEFCLASS;
224
225	switch (cgetent(&lc->lc_cap, login_dbarray, (char*)name)) {
226	case -1:		/* Failed, entry does not exist */
227	    if (strcmp(name, LOGIN_MECLASS) == 0)
228		break;	/* Don't retry default on 'me' */
229	    if (i == 0)
230	        r = -1;
231	    else if ((r = open(login_dbarray[0], O_RDONLY)) >= 0)
232	        close(r);
233	    /*
234	     * If there's at least one login class database,
235	     * and we aren't searching for a default class
236	     * then complain about a non-existent class.
237	     */
238	    if (r >= 0 || strcmp(name, LOGIN_DEFCLASS) != 0)
239		syslog(LOG_ERR, "login_getclass: unknown class '%s'", name);
240	    /* fall-back to default class */
241	    name = LOGIN_DEFCLASS;
242	    msg = "%s: no default/fallback class '%s'";
243	    if (cgetent(&lc->lc_cap, login_dbarray, (char*)name) != 0 && r >= 0)
244		break;
245	    /* Fallthru - just return system defaults */
246	case 0:		/* success! */
247	    if ((lc->lc_class = strdup(name)) != NULL) {
248		if (dir) {
249		    (void)seteuid(euid);
250		    (void)setegid(egid);
251		}
252		++lc_object_count;
253		return lc;
254	    }
255	    msg = "%s: strdup: %m";
256	    break;
257	case -2:
258	    msg = "%s: retrieving class information: %m";
259	    break;
260	case -3:
261	    msg = "%s: 'tc=' reference loop '%s'";
262	    break;
263	case 1:
264	    msg = "couldn't resolve 'tc=' reference in '%s'";
265	    break;
266	default:
267	    msg = "%s: unexpected cgetent() error '%s': %m";
268	    break;
269	}
270	if (dir) {
271	    (void)seteuid(euid);
272	    (void)setegid(egid);
273	}
274	if (msg != NULL)
275	    syslog(LOG_ERR, msg, "login_getclass", name);
276	free(lc);
277    }
278
279    return NULL;
280}
281
282
283
284/*
285 * login_getclass()
286 * Get the login class for the system (only) login class database.
287 * Return a filled-out login_cap_t structure, including
288 * class name, and the capability record buffer.
289 */
290
291login_cap_t *
292login_getclass(const char *cls)
293{
294    return login_getclassbyname(cls, NULL);
295}
296
297
298/*
299 * login_getclass()
300 * Get the login class for a given password entry from
301 * the system (only) login class database.
302 * If the password entry's class field is not set, or
303 * the class specified does not exist, then use the
304 * default of LOGIN_DEFCLASS (ie. "default").
305 * Return a filled-out login_cap_t structure, including
306 * class name, and the capability record buffer.
307 */
308
309login_cap_t *
310login_getpwclass(const struct passwd *pwd)
311{
312    const char	*cls = NULL;
313
314    if (pwd != NULL) {
315	cls = pwd->pw_class;
316	if (cls == NULL || *cls == '\0')
317	    cls = (pwd->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS;
318    }
319    return login_getclassbyname(cls, pwd);
320}
321
322
323/*
324 * login_getuserclass()
325 * Get the login class for a given password entry, allowing user
326 * overrides via ~/.login_conf.
327 */
328
329login_cap_t *
330login_getuserclass(const struct passwd *pwd)
331{
332    return login_getclassbyname(LOGIN_MECLASS, pwd);
333}
334
335
336
337/*
338 * login_getcapstr()
339 * Given a login_cap entry, and a capability name, return the
340 * value defined for that capability, a defualt if not found, or
341 * an error string on error.
342 */
343
344char *
345login_getcapstr(login_cap_t *lc, const char *cap, char *def, char *error)
346{
347    char    *res;
348    int	    ret;
349
350    if (lc == NULL || cap == NULL || lc->lc_cap == NULL || *cap == '\0')
351	return def;
352
353    if ((ret = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1)
354	return def;
355    return (ret >= 0) ? res : error;
356}
357
358
359/*
360 * login_getcaplist()
361 * Given a login_cap entry, and a capability name, return the
362 * value defined for that capability split into an array of
363 * strings.
364 */
365
366char **
367login_getcaplist(login_cap_t *lc, const char *cap, const char *chars)
368{
369    char    *lstring;
370
371    if (chars == NULL)
372	chars = ", \t";
373    if ((lstring = login_getcapstr(lc, (char*)cap, NULL, NULL)) != NULL)
374	return arrayize(lstring, chars, NULL);
375    return NULL;
376}
377
378
379/*
380 * login_getpath()
381 * From the login_cap_t <lc>, get the capability <cap> which is
382 * formatted as either a space or comma delimited list of paths
383 * and append them all into a string and separate by semicolons.
384 * If there is an error of any kind, return <error>.
385 */
386
387char *
388login_getpath(login_cap_t *lc, const char *cap, char * error)
389{
390    char    *str;
391
392    if ((str = login_getcapstr(lc, (char*)cap, NULL, NULL)) == NULL)
393	str = error;
394    else {
395	char *ptr = str;
396
397	while (*ptr) {
398	    int count = strcspn(ptr, ", \t");
399	    ptr += count;
400	    if (*ptr)
401		*ptr++ = ':';
402	}
403    }
404    return str;
405}
406
407
408static int
409isinfinite(const char *s)
410{
411    static const char *infs[] = {
412	"infinity",
413	"inf",
414	"unlimited",
415	"unlimit",
416	"-1",
417	NULL
418    };
419    const char **i = &infs[0];
420
421    while (*i != NULL) {
422	if (strcasecmp(s, *i) == 0)
423	    return 1;
424	++i;
425    }
426    return 0;
427}
428
429
430static u_quad_t
431rmultiply(u_quad_t n1, u_quad_t n2)
432{
433    u_quad_t	m, r;
434    int		b1, b2;
435
436    static int bpw = 0;
437
438    /* Handle simple cases */
439    if (n1 == 0 || n2 == 0)
440	return 0;
441    if (n1 == 1)
442	return n2;
443    if (n2 == 1)
444	return n1;
445
446    /*
447     * sizeof() returns number of bytes needed for storage.
448     * This may be different from the actual number of useful bits.
449     */
450    if (!bpw) {
451	bpw = sizeof(u_quad_t) * 8;
452	while (((u_quad_t)1 << (bpw-1)) == 0)
453	    --bpw;
454    }
455
456    /*
457     * First check the magnitude of each number. If the sum of the
458     * magnatude is way to high, reject the number. (If this test
459     * is not done then the first multiply below may overflow.)
460     */
461    for (b1 = bpw; (((u_quad_t)1 << (b1-1)) & n1) == 0; --b1)
462	;
463    for (b2 = bpw; (((u_quad_t)1 << (b2-1)) & n2) == 0; --b2)
464	;
465    if (b1 + b2 - 2 > bpw) {
466	errno = ERANGE;
467	return (UQUAD_MAX);
468    }
469
470    /*
471     * Decompose the multiplication to be:
472     * h1 = n1 & ~1
473     * h2 = n2 & ~1
474     * l1 = n1 & 1
475     * l2 = n2 & 1
476     * (h1 + l1) * (h2 + l2)
477     * (h1 * h2) + (h1 * l2) + (l1 * h2) + (l1 * l2)
478     *
479     * Since h1 && h2 do not have the low bit set, we can then say:
480     *
481     * (h1>>1 * h2>>1 * 4) + ...
482     *
483     * So if (h1>>1 * h2>>1) > (1<<(bpw - 2)) then the result will
484     * overflow.
485     *
486     * Finally, if MAX - ((h1 * l2) + (l1 * h2) + (l1 * l2)) < (h1*h2)
487     * then adding in residual amout will cause an overflow.
488     */
489
490    m = (n1 >> 1) * (n2 >> 1);
491    if (m >= ((u_quad_t)1 << (bpw-2))) {
492	errno = ERANGE;
493	return (UQUAD_MAX);
494    }
495    m *= 4;
496
497    r = (n1 & n2 & 1)
498	+ (n2 & 1) * (n1 & ~(u_quad_t)1)
499	+ (n1 & 1) * (n2 & ~(u_quad_t)1);
500
501    if ((u_quad_t)(m + r) < m) {
502	errno = ERANGE;
503	return (UQUAD_MAX);
504    }
505    m += r;
506
507    return (m);
508}
509
510
511/*
512 * login_getcaptime()
513 * From the login_cap_t <lc>, get the capability <cap>, which is
514 * formatted as a time (e.g., "<cap>=10h3m2s").  If <cap> is not
515 * present in <lc>, return <def>; if there is an error of some kind,
516 * return <error>.
517 */
518
519rlim_t
520login_getcaptime(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error)
521{
522    char    *res, *ep, *oval;
523    int	    r;
524    rlim_t  tot;
525
526    errno = 0;
527    if (lc == NULL || lc->lc_cap == NULL)
528	return def;
529
530    /*
531     * Look for <cap> in lc_cap.
532     * If it's not there (-1), return <def>.
533     * If there's an error, return <error>.
534     */
535
536    if ((r = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1)
537	return def;
538    else if (r < 0) {
539	errno = ERANGE;
540	return error;
541    }
542
543    /* "inf" and "infinity" are special cases */
544    if (isinfinite(res))
545	return RLIM_INFINITY;
546
547    /*
548     * Now go through the string, turning something like 1h2m3s into
549     * an integral value.  Whee.
550     */
551
552    errno = 0;
553    tot = 0;
554    oval = res;
555    while (*res) {
556	rlim_t tim = strtoq(res, &ep, 0);
557	rlim_t mult = 1;
558
559	if (ep == NULL || ep == res || errno != 0) {
560	invalid:
561	    syslog(LOG_WARNING, "login_getcaptime: class '%s' bad value %s=%s",
562		   lc->lc_class, cap, oval);
563	    errno = ERANGE;
564	    return error;
565	}
566	/* Look for suffixes */
567	switch (*ep++) {
568	case 0:
569	    ep--;
570	    break;	/* end of string */
571	case 's': case 'S':	/* seconds */
572	    break;
573	case 'm': case 'M':	/* minutes */
574	    mult = 60;
575	    break;
576	case 'h': case 'H':	/* hours */
577	    mult = 60L * 60L;
578	    break;
579	case 'd': case 'D':	/* days */
580	    mult = 60L * 60L * 24L;
581	    break;
582	case 'w': case 'W':	/* weeks */
583	    mult = 60L * 60L * 24L * 7L;
584	    break;
585	case 'y': case 'Y':	/* 365-day years */
586	    mult = 60L * 60L * 24L * 365L;
587	    break;
588	default:
589	    goto invalid;
590	}
591	res = ep;
592	tot += rmultiply(tim, mult);
593	if (errno)
594	    goto invalid;
595    }
596
597    return tot;
598}
599
600
601/*
602 * login_getcapnum()
603 * From the login_cap_t <lc>, extract the numerical value <cap>.
604 * If it is not present, return <def> for a default, and return
605 * <error> if there is an error.
606 * Like login_getcaptime(), only it only converts to a number, not
607 * to a time; "infinity" and "inf" are 'special.'
608 */
609
610rlim_t
611login_getcapnum(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error)
612{
613    char    *ep, *res;
614    int	    r;
615    rlim_t  val;
616
617    if (lc == NULL || lc->lc_cap == NULL)
618	return def;
619
620    /*
621     * For BSDI compatibility, try for the tag=<val> first
622     */
623    if ((r = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) {
624	long	lval;
625	/* string capability not present, so try for tag#<val> as numeric */
626	if ((r = cgetnum(lc->lc_cap, (char *)cap, &lval)) == -1)
627	    return def; /* Not there, so return default */
628	else if (r >= 0)
629	    return (rlim_t)lval;
630    }
631
632    if (r < 0) {
633	errno = ERANGE;
634	return error;
635    }
636
637    if (isinfinite(res))
638	return RLIM_INFINITY;
639
640    errno = 0;
641    val = strtoq(res, &ep, 0);
642    if (ep == NULL || ep == res || errno != 0) {
643	syslog(LOG_WARNING, "login_getcapnum: class '%s' bad value %s=%s",
644	       lc->lc_class, cap, res);
645	errno = ERANGE;
646	return error;
647    }
648
649    return val;
650}
651
652
653
654/*
655 * login_getcapsize()
656 * From the login_cap_t <lc>, extract the capability <cap>, which is
657 * formatted as a size (e.g., "<cap>=10M"); it can also be "infinity".
658 * If not present, return <def>, or <error> if there is an error of
659 * some sort.
660 */
661
662rlim_t
663login_getcapsize(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error)
664{
665    char    *ep, *res, *oval;
666    int	    r;
667    rlim_t  tot;
668
669    if (lc == NULL || lc->lc_cap == NULL)
670	return def;
671
672    if ((r = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1)
673	return def;
674    else if (r < 0) {
675	errno = ERANGE;
676	return error;
677    }
678
679    if (isinfinite(res))
680	return RLIM_INFINITY;
681
682    errno = 0;
683    tot = 0;
684    oval = res;
685    while (*res) {
686	rlim_t siz = strtoq(res, &ep, 0);
687	rlim_t mult = 1;
688
689	if (ep == NULL || ep == res || errno != 0) {
690	invalid:
691	    syslog(LOG_WARNING, "login_getcapsize: class '%s' bad value %s=%s",
692		   lc->lc_class, cap, oval);
693	    errno = ERANGE;
694	    return error;
695	}
696	switch (*ep++) {
697	case 0:	/* end of string */
698	    ep--;
699	    break;
700	case 'b': case 'B':	/* 512-byte blocks */
701	    mult = 512;
702	    break;
703	case 'k': case 'K':	/* 1024-byte Kilobytes */
704	    mult = 1024;
705	    break;
706	case 'm': case 'M':	/* 1024-k kbytes */
707	    mult = 1024 * 1024;
708	    break;
709	case 'g': case 'G':	/* 1Gbyte */
710	    mult = 1024 * 1024 * 1024;
711	    break;
712	case 't': case 'T':	/* 1TBte */
713	    mult = 1024LL * 1024LL * 1024LL * 1024LL;
714	    break;
715	default:
716	    goto invalid;
717	}
718	res = ep;
719	tot += rmultiply(siz, mult);
720	if (errno)
721	    goto invalid;
722    }
723
724    return tot;
725}
726
727
728/*
729 * login_getcapbool()
730 * From the login_cap_t <lc>, check for the existance of the capability
731 * of <cap>.  Return <def> if <lc>->lc_cap is NULL, otherwise return
732 * the whether or not <cap> exists there.
733 */
734
735int
736login_getcapbool(login_cap_t *lc, const char *cap, int def)
737{
738    if (lc == NULL || lc->lc_cap == NULL)
739	return def;
740    return (cgetcap(lc->lc_cap, (char *)cap, ':') != NULL);
741}
742
743
744/*
745 * login_getstyle()
746 * Given a login_cap entry <lc>, and optionally a type of auth <auth>,
747 * and optionally a style <style>, find the style that best suits these
748 * rules:
749 *	1.  If <auth> is non-null, look for an "auth-<auth>=" string
750 *	in the capability; if not present, default to "auth=".
751 *	2.  If there is no auth list found from (1), default to
752 *	"passwd" as an authorization list.
753 *	3.  If <style> is non-null, look for <style> in the list of
754 *	authorization methods found from (2); if <style> is NULL, default
755 *	to LOGIN_DEFSTYLE ("passwd").
756 *	4.  If the chosen style is found in the chosen list of authorization
757 *	methods, return that; otherwise, return NULL.
758 * E.g.:
759 *     login_getstyle(lc, NULL, "ftp");
760 *     login_getstyle(lc, "login", NULL);
761 *     login_getstyle(lc, "skey", "network");
762 */
763
764char *
765login_getstyle(login_cap_t *lc, char *style, const char *auth)
766{
767    int	    i;
768    char    **authtypes = NULL;
769    char    *auths= NULL;
770    char    realauth[64];
771
772    static char *defauthtypes[] = { LOGIN_DEFSTYLE, NULL };
773
774    if (auth != NULL && *auth != '\0') {
775	if (snprintf(realauth, sizeof realauth, "auth-%s", auth) < sizeof realauth)
776	    authtypes = login_getcaplist(lc, realauth, NULL);
777    }
778
779    if (authtypes == NULL)
780	authtypes = login_getcaplist(lc, "auth", NULL);
781
782    if (authtypes == NULL)
783	authtypes = defauthtypes;
784
785    /*
786     * We have at least one authtype now; auths is a comma-separated
787     * (or space-separated) list of authentication types.  We have to
788     * convert from this to an array of char*'s; authtypes then gets this.
789     */
790    i = 0;
791    if (style != NULL && *style != '\0') {
792	while (authtypes[i] != NULL && strcmp(style, authtypes[i]) != 0)
793	    i++;
794    }
795
796    lc->lc_style = NULL;
797    if (authtypes[i] != NULL && (auths = strdup(authtypes[i])) != NULL)
798	lc->lc_style = auths;
799
800    if (lc->lc_style != NULL)
801	lc->lc_style = strdup(lc->lc_style);
802
803    return lc->lc_style;
804}
805