168bbf3adSDavid Nugent /*- 268bbf3adSDavid Nugent * Copyright (c) 1996 by 368bbf3adSDavid Nugent * Sean Eric Fagan <sef@kithrup.com> 468bbf3adSDavid Nugent * David Nugent <davidn@blaze.net.au> 568bbf3adSDavid Nugent * All rights reserved. 668bbf3adSDavid Nugent * 756c04344SDavid Nugent * Portions copyright (c) 1995,1997 856c04344SDavid Nugent * Berkeley Software Design, Inc. 956c04344SDavid Nugent * All rights reserved. 1056c04344SDavid Nugent * 1168bbf3adSDavid Nugent * Redistribution and use in source and binary forms, with or without 1268bbf3adSDavid Nugent * modification, is permitted provided that the following conditions 1368bbf3adSDavid Nugent * are met: 1468bbf3adSDavid Nugent * 1. Redistributions of source code must retain the above copyright 1568bbf3adSDavid Nugent * notice immediately at the beginning of the file, without modification, 1668bbf3adSDavid Nugent * this list of conditions, and the following disclaimer. 1768bbf3adSDavid Nugent * 2. Redistributions in binary form must reproduce the above copyright 1868bbf3adSDavid Nugent * notice, this list of conditions and the following disclaimer in the 1968bbf3adSDavid Nugent * documentation and/or other materials provided with the distribution. 2068bbf3adSDavid Nugent * 3. This work was done expressly for inclusion into FreeBSD. Other use 2168bbf3adSDavid Nugent * is permitted provided this notation is included. 2268bbf3adSDavid Nugent * 4. Absolutely no warranty of function or purpose is made by the authors. 2368bbf3adSDavid Nugent * 5. Modifications may be freely made to this file providing the above 2468bbf3adSDavid Nugent * conditions are met. 2568bbf3adSDavid Nugent * 2668bbf3adSDavid Nugent * Low-level routines relating to the user capabilities database 2768bbf3adSDavid Nugent */ 2868bbf3adSDavid Nugent 298719c58fSMatthew Dillon #include <sys/cdefs.h> 308719c58fSMatthew Dillon __FBSDID("$FreeBSD$"); 318719c58fSMatthew Dillon 3268bbf3adSDavid Nugent #include <sys/types.h> 3368bbf3adSDavid Nugent #include <sys/time.h> 3468bbf3adSDavid Nugent #include <sys/resource.h> 3568bbf3adSDavid Nugent #include <sys/param.h> 360ebec5d3SMark Murray #include <errno.h> 370ebec5d3SMark Murray #include <fcntl.h> 3856c04344SDavid Nugent #include <libutil.h> 3968bbf3adSDavid Nugent #include <login_cap.h> 400ebec5d3SMark Murray #include <pwd.h> 410ebec5d3SMark Murray #include <stdio.h> 420ebec5d3SMark Murray #include <stdlib.h> 430ebec5d3SMark Murray #include <string.h> 440ebec5d3SMark Murray #include <syslog.h> 450ebec5d3SMark Murray #include <unistd.h> 4668bbf3adSDavid Nugent 4756c04344SDavid Nugent /* 4856c04344SDavid Nugent * allocstr() 4956c04344SDavid Nugent * Manage a single static pointer for handling a local char* buffer, 5056c04344SDavid Nugent * resizing as necessary to contain the string. 5156c04344SDavid Nugent * 5256c04344SDavid Nugent * allocarray() 5356c04344SDavid Nugent * Manage a static array for handling a group of strings, resizing 5456c04344SDavid Nugent * when necessary. 5556c04344SDavid Nugent */ 5668bbf3adSDavid Nugent 5768bbf3adSDavid Nugent static int lc_object_count = 0; 5868bbf3adSDavid Nugent 5968bbf3adSDavid Nugent static size_t internal_stringsz = 0; 6068bbf3adSDavid Nugent static char * internal_string = NULL; 6168bbf3adSDavid Nugent static size_t internal_arraysz = 0; 62547fa0d9SMark Murray static const char ** internal_array = NULL; 6368bbf3adSDavid Nugent 64b8a5cd86SDag-Erling Smørgrav static char path_login_conf[] = _PATH_LOGIN_CONF; 65b8a5cd86SDag-Erling Smørgrav 6668bbf3adSDavid Nugent static char * 67547fa0d9SMark Murray allocstr(const char *str) 6868bbf3adSDavid Nugent { 6968bbf3adSDavid Nugent char *p; 7056c04344SDavid Nugent 7168bbf3adSDavid Nugent size_t sz = strlen(str) + 1; /* realloc() only if necessary */ 7268bbf3adSDavid Nugent if (sz <= internal_stringsz) 731c594de5SDavid Nugent p = strcpy(internal_string, str); 7468bbf3adSDavid Nugent else if ((p = realloc(internal_string, sz)) != NULL) { 7568bbf3adSDavid Nugent internal_stringsz = sz; 7668bbf3adSDavid Nugent internal_string = strcpy(p, str); 7768bbf3adSDavid Nugent } 7868bbf3adSDavid Nugent return p; 7968bbf3adSDavid Nugent } 8068bbf3adSDavid Nugent 8156c04344SDavid Nugent 82547fa0d9SMark Murray static const char ** 8368bbf3adSDavid Nugent allocarray(size_t sz) 8468bbf3adSDavid Nugent { 85547fa0d9SMark Murray static const char **p; 8656c04344SDavid Nugent 8768bbf3adSDavid Nugent if (sz <= internal_arraysz) 8868bbf3adSDavid Nugent p = internal_array; 8968bbf3adSDavid Nugent else if ((p = realloc(internal_array, sz * sizeof(char*))) != NULL) { 9068bbf3adSDavid Nugent internal_arraysz = sz; 9168bbf3adSDavid Nugent internal_array = p; 9268bbf3adSDavid Nugent } 9368bbf3adSDavid Nugent return p; 9468bbf3adSDavid Nugent } 9568bbf3adSDavid Nugent 9668bbf3adSDavid Nugent 9768bbf3adSDavid Nugent /* 9868bbf3adSDavid Nugent * arrayize() 999a01d32bSJeroen Ruigrok van der Werven * Turn a simple string <str> separated by any of 10068bbf3adSDavid Nugent * the set of <chars> into an array. The last element 10168bbf3adSDavid Nugent * of the array will be NULL, as is proper. 10268bbf3adSDavid Nugent * Free using freearraystr() 10368bbf3adSDavid Nugent */ 10468bbf3adSDavid Nugent 105547fa0d9SMark Murray static const char ** 106547fa0d9SMark Murray arrayize(const char *str, const char *chars, int *size) 10768bbf3adSDavid Nugent { 10868bbf3adSDavid Nugent int i; 10905bcd1d0SPeter Wemm char *ptr; 11005bcd1d0SPeter Wemm const char *cptr; 111547fa0d9SMark Murray const char **res = NULL; 11268bbf3adSDavid Nugent 11356c04344SDavid Nugent /* count the sub-strings */ 11405bcd1d0SPeter Wemm for (i = 0, cptr = str; *cptr; i++) { 11505bcd1d0SPeter Wemm int count = strcspn(cptr, chars); 11605bcd1d0SPeter Wemm cptr += count; 11705bcd1d0SPeter Wemm if (*cptr) 11805bcd1d0SPeter Wemm ++cptr; 11968bbf3adSDavid Nugent } 12068bbf3adSDavid Nugent 12156c04344SDavid Nugent /* alloc the array */ 12256c04344SDavid Nugent if ((ptr = allocstr(str)) != NULL) { 12356c04344SDavid Nugent if ((res = allocarray(++i)) == NULL) 12405bcd1d0SPeter Wemm free((void *)(uintptr_t)(const void *)str); 12556c04344SDavid Nugent else { 12656c04344SDavid Nugent /* now split the string */ 1275f3a7367SDavid Nugent i = 0; 12856c04344SDavid Nugent while (*ptr) { 12968bbf3adSDavid Nugent int count = strcspn(ptr, chars); 13056c04344SDavid Nugent res[i++] = ptr; 13168bbf3adSDavid Nugent ptr += count; 13268bbf3adSDavid Nugent if (*ptr) 13368bbf3adSDavid Nugent *ptr++ = '\0'; 13468bbf3adSDavid Nugent } 13556c04344SDavid Nugent res[i] = NULL; 13668bbf3adSDavid Nugent } 13756c04344SDavid Nugent } 13856c04344SDavid Nugent 13968bbf3adSDavid Nugent if (size) 14068bbf3adSDavid Nugent *size = i; 14156c04344SDavid Nugent 14268bbf3adSDavid Nugent return res; 14368bbf3adSDavid Nugent } 14468bbf3adSDavid Nugent 14556c04344SDavid Nugent 14668bbf3adSDavid Nugent /* 14768bbf3adSDavid Nugent * login_close() 14868bbf3adSDavid Nugent * Frees up all resources relating to a login class 14968bbf3adSDavid Nugent * 15068bbf3adSDavid Nugent */ 15168bbf3adSDavid Nugent 15268bbf3adSDavid Nugent void 15368bbf3adSDavid Nugent login_close(login_cap_t * lc) 15468bbf3adSDavid Nugent { 15568bbf3adSDavid Nugent if (lc) { 15668bbf3adSDavid Nugent free(lc->lc_style); 15768bbf3adSDavid Nugent free(lc->lc_class); 1583f81737fSTim Vanderhoek free(lc->lc_cap); 15968bbf3adSDavid Nugent free(lc); 16068bbf3adSDavid Nugent if (--lc_object_count == 0) { 16168bbf3adSDavid Nugent free(internal_string); 1622955fda4SDavid Nugent free(internal_array); 16368bbf3adSDavid Nugent internal_array = NULL; 16451706563SDavid Nugent internal_arraysz = 0; 16568bbf3adSDavid Nugent internal_string = NULL; 16651706563SDavid Nugent internal_stringsz = 0; 16768bbf3adSDavid Nugent cgetclose(); 16868bbf3adSDavid Nugent } 16968bbf3adSDavid Nugent } 17068bbf3adSDavid Nugent } 17168bbf3adSDavid Nugent 17268bbf3adSDavid Nugent 17368bbf3adSDavid Nugent /* 174a7429823SYaroslav Tykhiy * login_getclassbyname() 175a7429823SYaroslav Tykhiy * Get the login class by its name. 17668bbf3adSDavid Nugent * If the name given is NULL or empty, the default class 177a7429823SYaroslav Tykhiy * LOGIN_DEFCLASS (i.e., "default") is fetched. 178a7429823SYaroslav Tykhiy * If the name given is LOGIN_MECLASS and 17994cf9da0SDiomidis Spinellis * 'pwd' argument is non-NULL and contains an non-NULL 18094cf9da0SDiomidis Spinellis * dir entry, then the file _FILE_LOGIN_CONF is picked 18194cf9da0SDiomidis Spinellis * up from that directory and used before the system 182a7429823SYaroslav Tykhiy * login database. In that case the system login database 183a7429823SYaroslav Tykhiy * is looked up using LOGIN_MECLASS, too, which is a bug. 18468bbf3adSDavid Nugent * Return a filled-out login_cap_t structure, including 18568bbf3adSDavid Nugent * class name, and the capability record buffer. 18668bbf3adSDavid Nugent */ 18768bbf3adSDavid Nugent 18868bbf3adSDavid Nugent login_cap_t * 18956c04344SDavid Nugent login_getclassbyname(char const *name, const struct passwd *pwd) 19068bbf3adSDavid Nugent { 19156c04344SDavid Nugent login_cap_t *lc; 19268bbf3adSDavid Nugent 19356c04344SDavid Nugent if ((lc = malloc(sizeof(login_cap_t))) != NULL) { 194e36b245cSAndrey A. Chernov int r, me, i = 0; 1958a2deaa1SAndrey A. Chernov uid_t euid = 0; 1968a2deaa1SAndrey A. Chernov gid_t egid = 0; 19756c04344SDavid Nugent const char *msg = NULL; 198e36b245cSAndrey A. Chernov const char *dir; 19968bbf3adSDavid Nugent char userpath[MAXPATHLEN]; 20056c04344SDavid Nugent 20105bcd1d0SPeter Wemm static char *login_dbarray[] = { NULL, NULL, NULL }; 20268bbf3adSDavid Nugent 203e36b245cSAndrey A. Chernov me = (name != NULL && strcmp(name, LOGIN_MECLASS) == 0); 204e36b245cSAndrey A. Chernov dir = (!me || pwd == NULL) ? NULL : pwd->pw_dir; 2058df8b73fSRobert Watson /* 2068df8b73fSRobert Watson * Switch to user mode before checking/reading its ~/.login_conf 2078df8b73fSRobert Watson * - some NFSes have root read access disabled. 2088df8b73fSRobert Watson * 2098df8b73fSRobert Watson * XXX: This fails to configure additional groups. 2108df8b73fSRobert Watson */ 2118a2deaa1SAndrey A. Chernov if (dir) { 212ae702574SAndrey A. Chernov euid = geteuid(); 213ae702574SAndrey A. Chernov egid = getegid(); 214ae702574SAndrey A. Chernov (void)setegid(pwd->pw_gid); 215ae702574SAndrey A. Chernov (void)seteuid(pwd->pw_uid); 2168a2deaa1SAndrey A. Chernov } 217ae702574SAndrey A. Chernov 21856c04344SDavid Nugent if (dir && snprintf(userpath, MAXPATHLEN, "%s/%s", dir, 21956c04344SDavid Nugent _FILE_LOGIN_CONF) < MAXPATHLEN) { 22056c04344SDavid Nugent if (_secure_path(userpath, pwd->pw_uid, pwd->pw_gid) != -1) 221b8a5cd86SDag-Erling Smørgrav login_dbarray[i++] = userpath; 22256c04344SDavid Nugent } 223a7429823SYaroslav Tykhiy /* 224a7429823SYaroslav Tykhiy * XXX: Why to add the system database if the class is `me'? 225a7429823SYaroslav Tykhiy */ 226b8a5cd86SDag-Erling Smørgrav if (_secure_path(path_login_conf, 0, 0) != -1) 227b8a5cd86SDag-Erling Smørgrav login_dbarray[i++] = path_login_conf; 22868bbf3adSDavid Nugent login_dbarray[i] = NULL; 22968bbf3adSDavid Nugent 23056c04344SDavid Nugent memset(lc, 0, sizeof(login_cap_t)); 23168bbf3adSDavid Nugent lc->lc_cap = lc->lc_class = lc->lc_style = NULL; 23268bbf3adSDavid Nugent 23356c04344SDavid Nugent if (name == NULL || *name == '\0') 23456c04344SDavid Nugent name = LOGIN_DEFCLASS; 23568bbf3adSDavid Nugent 236547fa0d9SMark Murray switch (cgetent(&lc->lc_cap, login_dbarray, name)) { 23756c04344SDavid Nugent case -1: /* Failed, entry does not exist */ 238e36b245cSAndrey A. Chernov if (me) 23956c04344SDavid Nugent break; /* Don't retry default on 'me' */ 24056c04344SDavid Nugent if (i == 0) 24156c04344SDavid Nugent r = -1; 242*d1d4d952SJilles Tjoelker else if ((r = open(login_dbarray[0], O_RDONLY | O_CLOEXEC)) >= 0) 24356c04344SDavid Nugent close(r); 24456c04344SDavid Nugent /* 24556c04344SDavid Nugent * If there's at least one login class database, 24656c04344SDavid Nugent * and we aren't searching for a default class 24756c04344SDavid Nugent * then complain about a non-existent class. 24856c04344SDavid Nugent */ 24956c04344SDavid Nugent if (r >= 0 || strcmp(name, LOGIN_DEFCLASS) != 0) 25056c04344SDavid Nugent syslog(LOG_ERR, "login_getclass: unknown class '%s'", name); 25156c04344SDavid Nugent /* fall-back to default class */ 25256c04344SDavid Nugent name = LOGIN_DEFCLASS; 25356c04344SDavid Nugent msg = "%s: no default/fallback class '%s'"; 254547fa0d9SMark Murray if (cgetent(&lc->lc_cap, login_dbarray, name) != 0 && r >= 0) 25556c04344SDavid Nugent break; 2567fed38d0SPhilippe Charnier /* FALLTHROUGH - just return system defaults */ 25756c04344SDavid Nugent case 0: /* success! */ 25856c04344SDavid Nugent if ((lc->lc_class = strdup(name)) != NULL) { 2598a2deaa1SAndrey A. Chernov if (dir) { 260ae702574SAndrey A. Chernov (void)seteuid(euid); 261ae702574SAndrey A. Chernov (void)setegid(egid); 2628a2deaa1SAndrey A. Chernov } 26356c04344SDavid Nugent ++lc_object_count; 26468bbf3adSDavid Nugent return lc; 26568bbf3adSDavid Nugent } 26656c04344SDavid Nugent msg = "%s: strdup: %m"; 26756c04344SDavid Nugent break; 26856c04344SDavid Nugent case -2: 26956c04344SDavid Nugent msg = "%s: retrieving class information: %m"; 27056c04344SDavid Nugent break; 27156c04344SDavid Nugent case -3: 27256c04344SDavid Nugent msg = "%s: 'tc=' reference loop '%s'"; 27356c04344SDavid Nugent break; 27456c04344SDavid Nugent case 1: 27556c04344SDavid Nugent msg = "couldn't resolve 'tc=' reference in '%s'"; 27656c04344SDavid Nugent break; 27756c04344SDavid Nugent default: 27856c04344SDavid Nugent msg = "%s: unexpected cgetent() error '%s': %m"; 27956c04344SDavid Nugent break; 28056c04344SDavid Nugent } 2818a2deaa1SAndrey A. Chernov if (dir) { 282ae702574SAndrey A. Chernov (void)seteuid(euid); 283ae702574SAndrey A. Chernov (void)setegid(egid); 2848a2deaa1SAndrey A. Chernov } 28556c04344SDavid Nugent if (msg != NULL) 28656c04344SDavid Nugent syslog(LOG_ERR, msg, "login_getclass", name); 28756c04344SDavid Nugent free(lc); 28856c04344SDavid Nugent } 28968bbf3adSDavid Nugent 29056c04344SDavid Nugent return NULL; 29156c04344SDavid Nugent } 29256c04344SDavid Nugent 29356c04344SDavid Nugent 29456c04344SDavid Nugent 29556c04344SDavid Nugent /* 29656c04344SDavid Nugent * login_getclass() 29756c04344SDavid Nugent * Get the login class for the system (only) login class database. 29856c04344SDavid Nugent * Return a filled-out login_cap_t structure, including 29956c04344SDavid Nugent * class name, and the capability record buffer. 30056c04344SDavid Nugent */ 30156c04344SDavid Nugent 30256c04344SDavid Nugent login_cap_t * 30356c04344SDavid Nugent login_getclass(const char *cls) 30456c04344SDavid Nugent { 30556c04344SDavid Nugent return login_getclassbyname(cls, NULL); 30656c04344SDavid Nugent } 30768bbf3adSDavid Nugent 30868bbf3adSDavid Nugent 30968bbf3adSDavid Nugent /* 310a7429823SYaroslav Tykhiy * login_getpwclass() 31168bbf3adSDavid Nugent * Get the login class for a given password entry from 31268bbf3adSDavid Nugent * the system (only) login class database. 31368bbf3adSDavid Nugent * If the password entry's class field is not set, or 31468bbf3adSDavid Nugent * the class specified does not exist, then use the 315a7429823SYaroslav Tykhiy * default of LOGIN_DEFCLASS (i.e., "default") for an unprivileged 316a7429823SYaroslav Tykhiy * user or that of LOGIN_DEFROOTCLASS (i.e., "root") for a super-user. 31768bbf3adSDavid Nugent * Return a filled-out login_cap_t structure, including 31868bbf3adSDavid Nugent * class name, and the capability record buffer. 31968bbf3adSDavid Nugent */ 32068bbf3adSDavid Nugent 32168bbf3adSDavid Nugent login_cap_t * 32256c04344SDavid Nugent login_getpwclass(const struct passwd *pwd) 32368bbf3adSDavid Nugent { 32456c04344SDavid Nugent const char *cls = NULL; 32556c04344SDavid Nugent 3261c594de5SDavid Nugent if (pwd != NULL) { 32756c04344SDavid Nugent cls = pwd->pw_class; 32856c04344SDavid Nugent if (cls == NULL || *cls == '\0') 32956c04344SDavid Nugent cls = (pwd->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS; 3302dc7d862SDavid Nugent } 331a7429823SYaroslav Tykhiy /* 332a7429823SYaroslav Tykhiy * XXX: pwd should be unused by login_getclassbyname() unless cls is `me', 333a7429823SYaroslav Tykhiy * so NULL can be passed instead of pwd for more safety. 334a7429823SYaroslav Tykhiy */ 33556c04344SDavid Nugent return login_getclassbyname(cls, pwd); 33668bbf3adSDavid Nugent } 33768bbf3adSDavid Nugent 33868bbf3adSDavid Nugent 33968bbf3adSDavid Nugent /* 34068bbf3adSDavid Nugent * login_getuserclass() 341a7429823SYaroslav Tykhiy * Get the `me' login class, allowing user overrides via ~/.login_conf. 342a7429823SYaroslav Tykhiy * Note that user overrides are allowed only in the `me' class. 34368bbf3adSDavid Nugent */ 34468bbf3adSDavid Nugent 34568bbf3adSDavid Nugent login_cap_t * 34668bbf3adSDavid Nugent login_getuserclass(const struct passwd *pwd) 34768bbf3adSDavid Nugent { 34856c04344SDavid Nugent return login_getclassbyname(LOGIN_MECLASS, pwd); 34968bbf3adSDavid Nugent } 35068bbf3adSDavid Nugent 35168bbf3adSDavid Nugent 35268bbf3adSDavid Nugent /* 35368bbf3adSDavid Nugent * login_getcapstr() 35468bbf3adSDavid Nugent * Given a login_cap entry, and a capability name, return the 355043d661dSTom Rhodes * value defined for that capability, a default if not found, or 35668bbf3adSDavid Nugent * an error string on error. 35768bbf3adSDavid Nugent */ 35868bbf3adSDavid Nugent 359b00ba4ccSRuslan Ermilov const char * 360b00ba4ccSRuslan Ermilov login_getcapstr(login_cap_t *lc, const char *cap, const char *def, const char *error) 36168bbf3adSDavid Nugent { 36268bbf3adSDavid Nugent char *res; 36368bbf3adSDavid Nugent int ret; 36468bbf3adSDavid Nugent 36568bbf3adSDavid Nugent if (lc == NULL || cap == NULL || lc->lc_cap == NULL || *cap == '\0') 36668bbf3adSDavid Nugent return def; 36768bbf3adSDavid Nugent 368547fa0d9SMark Murray if ((ret = cgetstr(lc->lc_cap, cap, &res)) == -1) 36968bbf3adSDavid Nugent return def; 37056c04344SDavid Nugent return (ret >= 0) ? res : error; 37168bbf3adSDavid Nugent } 37268bbf3adSDavid Nugent 37368bbf3adSDavid Nugent 37468bbf3adSDavid Nugent /* 37568bbf3adSDavid Nugent * login_getcaplist() 37668bbf3adSDavid Nugent * Given a login_cap entry, and a capability name, return the 37768bbf3adSDavid Nugent * value defined for that capability split into an array of 37868bbf3adSDavid Nugent * strings. 37968bbf3adSDavid Nugent */ 38068bbf3adSDavid Nugent 381547fa0d9SMark Murray const char ** 38268bbf3adSDavid Nugent login_getcaplist(login_cap_t *lc, const char *cap, const char *chars) 38368bbf3adSDavid Nugent { 384547fa0d9SMark Murray const char *lstring; 38568bbf3adSDavid Nugent 38668bbf3adSDavid Nugent if (chars == NULL) 387f01e3989SDavid Nugent chars = ", \t"; 388547fa0d9SMark Murray if ((lstring = login_getcapstr(lc, cap, NULL, NULL)) != NULL) 38968bbf3adSDavid Nugent return arrayize(lstring, chars, NULL); 39068bbf3adSDavid Nugent return NULL; 39168bbf3adSDavid Nugent } 39268bbf3adSDavid Nugent 39368bbf3adSDavid Nugent 39468bbf3adSDavid Nugent /* 39568bbf3adSDavid Nugent * login_getpath() 39668bbf3adSDavid Nugent * From the login_cap_t <lc>, get the capability <cap> which is 39768bbf3adSDavid Nugent * formatted as either a space or comma delimited list of paths 39868bbf3adSDavid Nugent * and append them all into a string and separate by semicolons. 39968bbf3adSDavid Nugent * If there is an error of any kind, return <error>. 40068bbf3adSDavid Nugent */ 40168bbf3adSDavid Nugent 402b00ba4ccSRuslan Ermilov const char * 403b00ba4ccSRuslan Ermilov login_getpath(login_cap_t *lc, const char *cap, const char *error) 40468bbf3adSDavid Nugent { 405b00ba4ccSRuslan Ermilov const char *str; 406547fa0d9SMark Murray char *ptr; 407547fa0d9SMark Murray int count; 40868bbf3adSDavid Nugent 409547fa0d9SMark Murray str = login_getcapstr(lc, cap, NULL, NULL); 410547fa0d9SMark Murray if (str == NULL) 411547fa0d9SMark Murray return error; 412547fa0d9SMark Murray ptr = __DECONST(char *, str); /* XXXX Yes, very dodgy */ 41368bbf3adSDavid Nugent while (*ptr) { 414547fa0d9SMark Murray count = strcspn(ptr, ", \t"); 41568bbf3adSDavid Nugent ptr += count; 41668bbf3adSDavid Nugent if (*ptr) 41768bbf3adSDavid Nugent *ptr++ = ':'; 41868bbf3adSDavid Nugent } 41968bbf3adSDavid Nugent return str; 42068bbf3adSDavid Nugent } 42168bbf3adSDavid Nugent 42268bbf3adSDavid Nugent 42356c04344SDavid Nugent static int 42456c04344SDavid Nugent isinfinite(const char *s) 42556c04344SDavid Nugent { 42656c04344SDavid Nugent static const char *infs[] = { 42756c04344SDavid Nugent "infinity", 42856c04344SDavid Nugent "inf", 42956c04344SDavid Nugent "unlimited", 43056c04344SDavid Nugent "unlimit", 43156c04344SDavid Nugent "-1", 43256c04344SDavid Nugent NULL 43356c04344SDavid Nugent }; 43456c04344SDavid Nugent const char **i = &infs[0]; 43556c04344SDavid Nugent 43656c04344SDavid Nugent while (*i != NULL) { 43756c04344SDavid Nugent if (strcasecmp(s, *i) == 0) 43856c04344SDavid Nugent return 1; 43956c04344SDavid Nugent ++i; 44056c04344SDavid Nugent } 44156c04344SDavid Nugent return 0; 44256c04344SDavid Nugent } 44356c04344SDavid Nugent 44456c04344SDavid Nugent 44556c04344SDavid Nugent static u_quad_t 44656c04344SDavid Nugent rmultiply(u_quad_t n1, u_quad_t n2) 44756c04344SDavid Nugent { 44856c04344SDavid Nugent u_quad_t m, r; 44956c04344SDavid Nugent int b1, b2; 45056c04344SDavid Nugent 45156c04344SDavid Nugent static int bpw = 0; 45256c04344SDavid Nugent 45356c04344SDavid Nugent /* Handle simple cases */ 45456c04344SDavid Nugent if (n1 == 0 || n2 == 0) 45556c04344SDavid Nugent return 0; 45656c04344SDavid Nugent if (n1 == 1) 45756c04344SDavid Nugent return n2; 45856c04344SDavid Nugent if (n2 == 1) 45956c04344SDavid Nugent return n1; 46056c04344SDavid Nugent 46156c04344SDavid Nugent /* 46256c04344SDavid Nugent * sizeof() returns number of bytes needed for storage. 46356c04344SDavid Nugent * This may be different from the actual number of useful bits. 46456c04344SDavid Nugent */ 46556c04344SDavid Nugent if (!bpw) { 46656c04344SDavid Nugent bpw = sizeof(u_quad_t) * 8; 46756c04344SDavid Nugent while (((u_quad_t)1 << (bpw-1)) == 0) 46856c04344SDavid Nugent --bpw; 46956c04344SDavid Nugent } 47056c04344SDavid Nugent 47156c04344SDavid Nugent /* 47256c04344SDavid Nugent * First check the magnitude of each number. If the sum of the 47356c04344SDavid Nugent * magnatude is way to high, reject the number. (If this test 47456c04344SDavid Nugent * is not done then the first multiply below may overflow.) 47556c04344SDavid Nugent */ 47656c04344SDavid Nugent for (b1 = bpw; (((u_quad_t)1 << (b1-1)) & n1) == 0; --b1) 47756c04344SDavid Nugent ; 47856c04344SDavid Nugent for (b2 = bpw; (((u_quad_t)1 << (b2-1)) & n2) == 0; --b2) 47956c04344SDavid Nugent ; 48056c04344SDavid Nugent if (b1 + b2 - 2 > bpw) { 48156c04344SDavid Nugent errno = ERANGE; 48256c04344SDavid Nugent return (UQUAD_MAX); 48356c04344SDavid Nugent } 48456c04344SDavid Nugent 48556c04344SDavid Nugent /* 48656c04344SDavid Nugent * Decompose the multiplication to be: 48756c04344SDavid Nugent * h1 = n1 & ~1 48856c04344SDavid Nugent * h2 = n2 & ~1 48956c04344SDavid Nugent * l1 = n1 & 1 49056c04344SDavid Nugent * l2 = n2 & 1 49156c04344SDavid Nugent * (h1 + l1) * (h2 + l2) 49256c04344SDavid Nugent * (h1 * h2) + (h1 * l2) + (l1 * h2) + (l1 * l2) 49356c04344SDavid Nugent * 49456c04344SDavid Nugent * Since h1 && h2 do not have the low bit set, we can then say: 49556c04344SDavid Nugent * 49656c04344SDavid Nugent * (h1>>1 * h2>>1 * 4) + ... 49756c04344SDavid Nugent * 49856c04344SDavid Nugent * So if (h1>>1 * h2>>1) > (1<<(bpw - 2)) then the result will 49956c04344SDavid Nugent * overflow. 50056c04344SDavid Nugent * 50156c04344SDavid Nugent * Finally, if MAX - ((h1 * l2) + (l1 * h2) + (l1 * l2)) < (h1*h2) 50256c04344SDavid Nugent * then adding in residual amout will cause an overflow. 50356c04344SDavid Nugent */ 50456c04344SDavid Nugent 50556c04344SDavid Nugent m = (n1 >> 1) * (n2 >> 1); 50656c04344SDavid Nugent if (m >= ((u_quad_t)1 << (bpw-2))) { 50756c04344SDavid Nugent errno = ERANGE; 50856c04344SDavid Nugent return (UQUAD_MAX); 50956c04344SDavid Nugent } 51056c04344SDavid Nugent m *= 4; 51156c04344SDavid Nugent 51256c04344SDavid Nugent r = (n1 & n2 & 1) 51356c04344SDavid Nugent + (n2 & 1) * (n1 & ~(u_quad_t)1) 51456c04344SDavid Nugent + (n1 & 1) * (n2 & ~(u_quad_t)1); 51556c04344SDavid Nugent 51656c04344SDavid Nugent if ((u_quad_t)(m + r) < m) { 51756c04344SDavid Nugent errno = ERANGE; 51856c04344SDavid Nugent return (UQUAD_MAX); 51956c04344SDavid Nugent } 52056c04344SDavid Nugent m += r; 52156c04344SDavid Nugent 52256c04344SDavid Nugent return (m); 52356c04344SDavid Nugent } 52456c04344SDavid Nugent 52556c04344SDavid Nugent 52668bbf3adSDavid Nugent /* 52768bbf3adSDavid Nugent * login_getcaptime() 52868bbf3adSDavid Nugent * From the login_cap_t <lc>, get the capability <cap>, which is 52968bbf3adSDavid Nugent * formatted as a time (e.g., "<cap>=10h3m2s"). If <cap> is not 53068bbf3adSDavid Nugent * present in <lc>, return <def>; if there is an error of some kind, 53168bbf3adSDavid Nugent * return <error>. 53268bbf3adSDavid Nugent */ 53368bbf3adSDavid Nugent 53468bbf3adSDavid Nugent rlim_t 53568bbf3adSDavid Nugent login_getcaptime(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) 53668bbf3adSDavid Nugent { 53756c04344SDavid Nugent char *res, *ep, *oval; 53856c04344SDavid Nugent int r; 53951706563SDavid Nugent rlim_t tot; 54068bbf3adSDavid Nugent 54168bbf3adSDavid Nugent errno = 0; 54268bbf3adSDavid Nugent if (lc == NULL || lc->lc_cap == NULL) 54368bbf3adSDavid Nugent return def; 54468bbf3adSDavid Nugent 54568bbf3adSDavid Nugent /* 54668bbf3adSDavid Nugent * Look for <cap> in lc_cap. 54768bbf3adSDavid Nugent * If it's not there (-1), return <def>. 54868bbf3adSDavid Nugent * If there's an error, return <error>. 54968bbf3adSDavid Nugent */ 55068bbf3adSDavid Nugent 551547fa0d9SMark Murray if ((r = cgetstr(lc->lc_cap, cap, &res)) == -1) 55268bbf3adSDavid Nugent return def; 55356c04344SDavid Nugent else if (r < 0) { 55456c04344SDavid Nugent errno = ERANGE; 55568bbf3adSDavid Nugent return error; 55656c04344SDavid Nugent } 55768bbf3adSDavid Nugent 55856c04344SDavid Nugent /* "inf" and "infinity" are special cases */ 55956c04344SDavid Nugent if (isinfinite(res)) 56068bbf3adSDavid Nugent return RLIM_INFINITY; 56168bbf3adSDavid Nugent 56268bbf3adSDavid Nugent /* 56368bbf3adSDavid Nugent * Now go through the string, turning something like 1h2m3s into 56468bbf3adSDavid Nugent * an integral value. Whee. 56568bbf3adSDavid Nugent */ 56668bbf3adSDavid Nugent 56768bbf3adSDavid Nugent errno = 0; 56851706563SDavid Nugent tot = 0; 56956c04344SDavid Nugent oval = res; 57068bbf3adSDavid Nugent while (*res) { 57156c04344SDavid Nugent rlim_t tim = strtoq(res, &ep, 0); 57256c04344SDavid Nugent rlim_t mult = 1; 57356c04344SDavid Nugent 57456c04344SDavid Nugent if (ep == NULL || ep == res || errno != 0) { 57556c04344SDavid Nugent invalid: 57656c04344SDavid Nugent syslog(LOG_WARNING, "login_getcaptime: class '%s' bad value %s=%s", 57756c04344SDavid Nugent lc->lc_class, cap, oval); 57856c04344SDavid Nugent errno = ERANGE; 57968bbf3adSDavid Nugent return error; 58068bbf3adSDavid Nugent } 58168bbf3adSDavid Nugent /* Look for suffixes */ 58268bbf3adSDavid Nugent switch (*ep++) { 58368bbf3adSDavid Nugent case 0: 58456c04344SDavid Nugent ep--; 58556c04344SDavid Nugent break; /* end of string */ 58668bbf3adSDavid Nugent case 's': case 'S': /* seconds */ 58768bbf3adSDavid Nugent break; 58868bbf3adSDavid Nugent case 'm': case 'M': /* minutes */ 58956c04344SDavid Nugent mult = 60; 59068bbf3adSDavid Nugent break; 59168bbf3adSDavid Nugent case 'h': case 'H': /* hours */ 59256c04344SDavid Nugent mult = 60L * 60L; 59368bbf3adSDavid Nugent break; 59468bbf3adSDavid Nugent case 'd': case 'D': /* days */ 59556c04344SDavid Nugent mult = 60L * 60L * 24L; 59668bbf3adSDavid Nugent break; 59768bbf3adSDavid Nugent case 'w': case 'W': /* weeks */ 59856c04344SDavid Nugent mult = 60L * 60L * 24L * 7L; 599c2043f40SDavid Nugent break; 60056c04344SDavid Nugent case 'y': case 'Y': /* 365-day years */ 60156c04344SDavid Nugent mult = 60L * 60L * 24L * 365L; 602c2043f40SDavid Nugent break; 60368bbf3adSDavid Nugent default: 60456c04344SDavid Nugent goto invalid; 60568bbf3adSDavid Nugent } 60668bbf3adSDavid Nugent res = ep; 60756c04344SDavid Nugent tot += rmultiply(tim, mult); 60856c04344SDavid Nugent if (errno) 60956c04344SDavid Nugent goto invalid; 61068bbf3adSDavid Nugent } 61156c04344SDavid Nugent 61268bbf3adSDavid Nugent return tot; 61368bbf3adSDavid Nugent } 61468bbf3adSDavid Nugent 61568bbf3adSDavid Nugent 61668bbf3adSDavid Nugent /* 61768bbf3adSDavid Nugent * login_getcapnum() 61868bbf3adSDavid Nugent * From the login_cap_t <lc>, extract the numerical value <cap>. 61968bbf3adSDavid Nugent * If it is not present, return <def> for a default, and return 62068bbf3adSDavid Nugent * <error> if there is an error. 62168bbf3adSDavid Nugent * Like login_getcaptime(), only it only converts to a number, not 62268bbf3adSDavid Nugent * to a time; "infinity" and "inf" are 'special.' 62368bbf3adSDavid Nugent */ 62468bbf3adSDavid Nugent 62568bbf3adSDavid Nugent rlim_t 62668bbf3adSDavid Nugent login_getcapnum(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) 62768bbf3adSDavid Nugent { 62868bbf3adSDavid Nugent char *ep, *res; 62956c04344SDavid Nugent int r; 63068bbf3adSDavid Nugent rlim_t val; 63168bbf3adSDavid Nugent 63268bbf3adSDavid Nugent if (lc == NULL || lc->lc_cap == NULL) 63368bbf3adSDavid Nugent return def; 63468bbf3adSDavid Nugent 63568bbf3adSDavid Nugent /* 63668bbf3adSDavid Nugent * For BSDI compatibility, try for the tag=<val> first 63768bbf3adSDavid Nugent */ 638547fa0d9SMark Murray if ((r = cgetstr(lc->lc_cap, cap, &res)) == -1) { 63968bbf3adSDavid Nugent long lval; 64056c04344SDavid Nugent /* string capability not present, so try for tag#<val> as numeric */ 641547fa0d9SMark Murray if ((r = cgetnum(lc->lc_cap, cap, &lval)) == -1) 64268bbf3adSDavid Nugent return def; /* Not there, so return default */ 64356c04344SDavid Nugent else if (r >= 0) 64468bbf3adSDavid Nugent return (rlim_t)lval; 64568bbf3adSDavid Nugent } 64668bbf3adSDavid Nugent 64756c04344SDavid Nugent if (r < 0) { 64856c04344SDavid Nugent errno = ERANGE; 64956c04344SDavid Nugent return error; 65056c04344SDavid Nugent } 65156c04344SDavid Nugent 65256c04344SDavid Nugent if (isinfinite(res)) 65368bbf3adSDavid Nugent return RLIM_INFINITY; 65468bbf3adSDavid Nugent 65568bbf3adSDavid Nugent errno = 0; 65656c04344SDavid Nugent val = strtoq(res, &ep, 0); 65756c04344SDavid Nugent if (ep == NULL || ep == res || errno != 0) { 65856c04344SDavid Nugent syslog(LOG_WARNING, "login_getcapnum: class '%s' bad value %s=%s", 65956c04344SDavid Nugent lc->lc_class, cap, res); 66056c04344SDavid Nugent errno = ERANGE; 66168bbf3adSDavid Nugent return error; 66256c04344SDavid Nugent } 66356c04344SDavid Nugent 66468bbf3adSDavid Nugent return val; 66568bbf3adSDavid Nugent } 66668bbf3adSDavid Nugent 66768bbf3adSDavid Nugent 66856c04344SDavid Nugent 66968bbf3adSDavid Nugent /* 67068bbf3adSDavid Nugent * login_getcapsize() 67168bbf3adSDavid Nugent * From the login_cap_t <lc>, extract the capability <cap>, which is 67268bbf3adSDavid Nugent * formatted as a size (e.g., "<cap>=10M"); it can also be "infinity". 67368bbf3adSDavid Nugent * If not present, return <def>, or <error> if there is an error of 67468bbf3adSDavid Nugent * some sort. 67568bbf3adSDavid Nugent */ 67668bbf3adSDavid Nugent 67768bbf3adSDavid Nugent rlim_t 67856c04344SDavid Nugent login_getcapsize(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) 67956c04344SDavid Nugent { 68056c04344SDavid Nugent char *ep, *res, *oval; 68156c04344SDavid Nugent int r; 68256c04344SDavid Nugent rlim_t tot; 68368bbf3adSDavid Nugent 68468bbf3adSDavid Nugent if (lc == NULL || lc->lc_cap == NULL) 68568bbf3adSDavid Nugent return def; 68668bbf3adSDavid Nugent 687547fa0d9SMark Murray if ((r = cgetstr(lc->lc_cap, cap, &res)) == -1) 68868bbf3adSDavid Nugent return def; 68956c04344SDavid Nugent else if (r < 0) { 69056c04344SDavid Nugent errno = ERANGE; 69168bbf3adSDavid Nugent return error; 69256c04344SDavid Nugent } 69368bbf3adSDavid Nugent 69456c04344SDavid Nugent if (isinfinite(res)) 69565f50b7fSAndrey A. Chernov return RLIM_INFINITY; 69665f50b7fSAndrey A. Chernov 69768bbf3adSDavid Nugent errno = 0; 69851706563SDavid Nugent tot = 0; 69956c04344SDavid Nugent oval = res; 70051706563SDavid Nugent while (*res) { 70156c04344SDavid Nugent rlim_t siz = strtoq(res, &ep, 0); 70256c04344SDavid Nugent rlim_t mult = 1; 70356c04344SDavid Nugent 70456c04344SDavid Nugent if (ep == NULL || ep == res || errno != 0) { 70556c04344SDavid Nugent invalid: 70656c04344SDavid Nugent syslog(LOG_WARNING, "login_getcapsize: class '%s' bad value %s=%s", 70756c04344SDavid Nugent lc->lc_class, cap, oval); 70856c04344SDavid Nugent errno = ERANGE; 70968bbf3adSDavid Nugent return error; 71056c04344SDavid Nugent } 711f9074e29SDavid Nugent switch (*ep++) { 71268bbf3adSDavid Nugent case 0: /* end of string */ 71351706563SDavid Nugent ep--; 71451706563SDavid Nugent break; 71568bbf3adSDavid Nugent case 'b': case 'B': /* 512-byte blocks */ 71656c04344SDavid Nugent mult = 512; 71756c04344SDavid Nugent break; 71868bbf3adSDavid Nugent case 'k': case 'K': /* 1024-byte Kilobytes */ 71956c04344SDavid Nugent mult = 1024; 72056c04344SDavid Nugent break; 72168bbf3adSDavid Nugent case 'm': case 'M': /* 1024-k kbytes */ 72256c04344SDavid Nugent mult = 1024 * 1024; 72356c04344SDavid Nugent break; 72468bbf3adSDavid Nugent case 'g': case 'G': /* 1Gbyte */ 72556c04344SDavid Nugent mult = 1024 * 1024 * 1024; 72656c04344SDavid Nugent break; 72768bbf3adSDavid Nugent case 't': case 'T': /* 1TBte */ 72856c04344SDavid Nugent mult = 1024LL * 1024LL * 1024LL * 1024LL; 72956c04344SDavid Nugent break; 73068bbf3adSDavid Nugent default: 73156c04344SDavid Nugent goto invalid; 73268bbf3adSDavid Nugent } 733f9074e29SDavid Nugent res = ep; 73456c04344SDavid Nugent tot += rmultiply(siz, mult); 73556c04344SDavid Nugent if (errno) 73656c04344SDavid Nugent goto invalid; 73751706563SDavid Nugent } 73856c04344SDavid Nugent 73951706563SDavid Nugent return tot; 74068bbf3adSDavid Nugent } 74168bbf3adSDavid Nugent 74268bbf3adSDavid Nugent 74368bbf3adSDavid Nugent /* 74468bbf3adSDavid Nugent * login_getcapbool() 74568bbf3adSDavid Nugent * From the login_cap_t <lc>, check for the existance of the capability 74668bbf3adSDavid Nugent * of <cap>. Return <def> if <lc>->lc_cap is NULL, otherwise return 74768bbf3adSDavid Nugent * the whether or not <cap> exists there. 74868bbf3adSDavid Nugent */ 74968bbf3adSDavid Nugent 75068bbf3adSDavid Nugent int 75168bbf3adSDavid Nugent login_getcapbool(login_cap_t *lc, const char *cap, int def) 75268bbf3adSDavid Nugent { 75368bbf3adSDavid Nugent if (lc == NULL || lc->lc_cap == NULL) 75468bbf3adSDavid Nugent return def; 755547fa0d9SMark Murray return (cgetcap(lc->lc_cap, cap, ':') != NULL); 75668bbf3adSDavid Nugent } 75768bbf3adSDavid Nugent 75868bbf3adSDavid Nugent 75968bbf3adSDavid Nugent /* 76068bbf3adSDavid Nugent * login_getstyle() 76168bbf3adSDavid Nugent * Given a login_cap entry <lc>, and optionally a type of auth <auth>, 76268bbf3adSDavid Nugent * and optionally a style <style>, find the style that best suits these 76368bbf3adSDavid Nugent * rules: 76468bbf3adSDavid Nugent * 1. If <auth> is non-null, look for an "auth-<auth>=" string 76568bbf3adSDavid Nugent * in the capability; if not present, default to "auth=". 76668bbf3adSDavid Nugent * 2. If there is no auth list found from (1), default to 76768bbf3adSDavid Nugent * "passwd" as an authorization list. 76868bbf3adSDavid Nugent * 3. If <style> is non-null, look for <style> in the list of 76968bbf3adSDavid Nugent * authorization methods found from (2); if <style> is NULL, default 77068bbf3adSDavid Nugent * to LOGIN_DEFSTYLE ("passwd"). 77168bbf3adSDavid Nugent * 4. If the chosen style is found in the chosen list of authorization 77268bbf3adSDavid Nugent * methods, return that; otherwise, return NULL. 77368bbf3adSDavid Nugent * E.g.: 77468bbf3adSDavid Nugent * login_getstyle(lc, NULL, "ftp"); 77568bbf3adSDavid Nugent * login_getstyle(lc, "login", NULL); 77668bbf3adSDavid Nugent * login_getstyle(lc, "skey", "network"); 77768bbf3adSDavid Nugent */ 77868bbf3adSDavid Nugent 779b00ba4ccSRuslan Ermilov const char * 780b00ba4ccSRuslan Ermilov login_getstyle(login_cap_t *lc, const char *style, const char *auth) 78168bbf3adSDavid Nugent { 78268bbf3adSDavid Nugent int i; 783547fa0d9SMark Murray const char **authtypes = NULL; 78468bbf3adSDavid Nugent char *auths= NULL; 78568bbf3adSDavid Nugent char realauth[64]; 78668bbf3adSDavid Nugent 787547fa0d9SMark Murray static const char *defauthtypes[] = { LOGIN_DEFSTYLE, NULL }; 78868bbf3adSDavid Nugent 78956c04344SDavid Nugent if (auth != NULL && *auth != '\0') { 790547fa0d9SMark Murray if (snprintf(realauth, sizeof realauth, "auth-%s", auth) < (int)sizeof(realauth)) 79168bbf3adSDavid Nugent authtypes = login_getcaplist(lc, realauth, NULL); 79256c04344SDavid Nugent } 79368bbf3adSDavid Nugent 79468bbf3adSDavid Nugent if (authtypes == NULL) 79568bbf3adSDavid Nugent authtypes = login_getcaplist(lc, "auth", NULL); 79668bbf3adSDavid Nugent 79768bbf3adSDavid Nugent if (authtypes == NULL) 79868bbf3adSDavid Nugent authtypes = defauthtypes; 79968bbf3adSDavid Nugent 80068bbf3adSDavid Nugent /* 8019a01d32bSJeroen Ruigrok van der Werven * We have at least one authtype now; auths is a comma-separated 80268bbf3adSDavid Nugent * (or space-separated) list of authentication types. We have to 80368bbf3adSDavid Nugent * convert from this to an array of char*'s; authtypes then gets this. 80468bbf3adSDavid Nugent */ 80568bbf3adSDavid Nugent i = 0; 80668bbf3adSDavid Nugent if (style != NULL && *style != '\0') { 80768bbf3adSDavid Nugent while (authtypes[i] != NULL && strcmp(style, authtypes[i]) != 0) 80868bbf3adSDavid Nugent i++; 80968bbf3adSDavid Nugent } 81056c04344SDavid Nugent 81168bbf3adSDavid Nugent lc->lc_style = NULL; 81268bbf3adSDavid Nugent if (authtypes[i] != NULL && (auths = strdup(authtypes[i])) != NULL) 81368bbf3adSDavid Nugent lc->lc_style = auths; 81468bbf3adSDavid Nugent 81556c04344SDavid Nugent if (lc->lc_style != NULL) 81656c04344SDavid Nugent lc->lc_style = strdup(lc->lc_style); 81756c04344SDavid Nugent 81868bbf3adSDavid Nugent return lc->lc_style; 81968bbf3adSDavid Nugent } 820