freebsd-dev/lib/libutil/login_cap.c

567 lines
14 KiB
C

/*-
* Copyright (c) 1996 by
* Sean Eric Fagan <sef@kithrup.com>
* David Nugent <davidn@blaze.net.au>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, is permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice immediately at the beginning of the file, without modification,
* this list of conditions, and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. This work was done expressly for inclusion into FreeBSD. Other use
* is permitted provided this notation is included.
* 4. Absolutely no warranty of function or purpose is made by the authors.
* 5. Modifications may be freely made to this file providing the above
* conditions are met.
*
* Low-level routines relating to the user capabilities database
*
* $FreeBSD$
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/param.h>
#include <pwd.h>
#include <login_cap.h>
#ifdef RLIM_LONG
# define STRTOV strtol
#else
# define STRTOV strtoq
#endif
static int lc_object_count = 0;
static size_t internal_stringsz = 0;
static char * internal_string = NULL;
static size_t internal_arraysz = 0;
static char ** internal_array = NULL;
static char *
allocstr(char * str)
{
char * p;
size_t sz = strlen(str) + 1; /* realloc() only if necessary */
if (sz <= internal_stringsz)
p = strcpy(internal_string, str);
else if ((p = realloc(internal_string, sz)) != NULL) {
internal_stringsz = sz;
internal_string = strcpy(p, str);
}
return p;
}
static char **
allocarray(size_t sz)
{
char ** p;
if (sz <= internal_arraysz)
p = internal_array;
else if ((p = realloc(internal_array, sz * sizeof(char*))) != NULL) {
internal_arraysz = sz;
internal_array = p;
}
return p;
}
/*
* arrayize()
* Turn a simple string <str> seperated by any of
* the set of <chars> into an array. The last element
* of the array will be NULL, as is proper.
* Free using freearraystr()
*/
static char **
arrayize(char *str, const char *chars, int *size)
{
int i;
char *ptr;
char **res = NULL;
for (i = 0, ptr = str; *ptr; i++) {
int count = strcspn(ptr, chars);
ptr += count;
if (*ptr)
++ptr;
}
if ((ptr = allocstr(str)) == NULL) {
res = NULL;
i = 0;
} else if ((res = allocarray(++i)) == NULL) {
free(str);
i = 0;
} else {
for (i = 0; *ptr; i++) {
int count = strcspn(ptr, chars);
res[i] = ptr;
ptr += count;
if (*ptr)
*ptr++ = '\0';
}
res[i] = 0;
}
if (size)
*size = i;
return res;
}
/*
* login_close()
* Frees up all resources relating to a login class
*
*/
void
login_close(login_cap_t * lc)
{
if (lc) {
free(lc->lc_style);
free(lc->lc_class);
free(lc);
if (--lc_object_count == 0) {
free(internal_string);
free(internal_array);
internal_array = NULL;
internal_arraysz = 0;
internal_string = NULL;
internal_stringsz = 0;
cgetclose();
}
}
}
/*
* login_getclassbyname() get the login class by its name.
* If the name given is NULL or empty, the default class
* LOGIN_DEFCLASS (ie. "default") is fetched. If the
* 'dir' argument contains a non-NULL non-empty string,
* then the file _FILE_LOGIN_CONF is picked up from that
* directory instead of the system login database.
* Return a filled-out login_cap_t structure, including
* class name, and the capability record buffer.
*/
login_cap_t *
login_getclassbyname(char const * name, char const * dir)
{
login_cap_t *lc = malloc(sizeof(login_cap_t));
if (lc != NULL) {
int i = 0;
char userpath[MAXPATHLEN];
static char *login_dbarray[] = { NULL, NULL, NULL };
if (dir && snprintf(userpath, MAXPATHLEN, "%s/%s", dir, _FILE_LOGIN_CONF) < MAXPATHLEN)
login_dbarray[i++] = userpath;
else
login_dbarray[i++] = _PATH_LOGIN_CONF;
login_dbarray[i ] = NULL;
lc->lc_cap = lc->lc_class = lc->lc_style = NULL;
if ((name == NULL || cgetent(&lc->lc_cap, login_dbarray, (char*)name) != 0) &&
cgetent(&lc->lc_cap, login_dbarray, (char*)(name = LOGIN_DEFCLASS)) != 0) {
free(lc);
lc = NULL;
} else {
++lc_object_count;
lc->lc_class = strdup(name);
}
}
return lc;
}
/*
* login_getclass()
* Get the login class for a given password entry from
* the system (only) login class database.
* If the password entry's class field is not set, or
* the class specified does not exist, then use the
* default of LOGIN_DEFCLASS (ie. "default").
* Return a filled-out login_cap_t structure, including
* class name, and the capability record buffer.
*/
login_cap_t *
login_getclass(const struct passwd *pwd)
{
const char * class = NULL;
if (pwd != NULL) {
if ((class = pwd->pw_class) == NULL || *class == '\0')
class = (pwd->pw_uid == 0) ? "root" : NULL;
}
return login_getclassbyname(class, 0);
}
/*
* login_getuserclass()
* Get the login class for a given password entry, allowing user
* overrides via ~/.login_conf.
* ### WAS: If the password entry's class field is not set,
* ####### or the class specified does not exist, then use
* If an entry with the recordid "me" does not exist, then use
* the default of LOGIN_DEFCLASS (ie. "default").
* Return a filled-out login_cap_t structure, including
* class name, and the capability record buffer.
*/
login_cap_t *
login_getuserclass(const struct passwd *pwd)
{
const char * class = "me"; /* (pwd == NULL) ? NULL : pwd->pw_class; */
const char * home = (pwd == NULL) ? NULL : pwd->pw_dir;
return login_getclassbyname(class, home);
}
/*
* login_getcapstr()
* Given a login_cap entry, and a capability name, return the
* value defined for that capability, a defualt if not found, or
* an error string on error.
*/
char *
login_getcapstr(login_cap_t *lc, const char *cap, char *def, char *error)
{
char *res;
int ret;
if (lc == NULL || cap == NULL || lc->lc_cap == NULL || *cap == '\0')
return def;
if ((ret = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) {
return def;
} else if (ret >= 0)
return res;
else
return error;
}
/*
* login_getcaplist()
* Given a login_cap entry, and a capability name, return the
* value defined for that capability split into an array of
* strings.
*/
char **
login_getcaplist(login_cap_t *lc, const char * cap, const char * chars)
{
char * lstring;
if (chars == NULL)
chars = ", \t";
if ((lstring = login_getcapstr(lc, (char*)cap, NULL, NULL)) != NULL)
return arrayize(lstring, chars, NULL);
return NULL;
}
/*
* login_getpath()
* From the login_cap_t <lc>, get the capability <cap> which is
* formatted as either a space or comma delimited list of paths
* and append them all into a string and separate by semicolons.
* If there is an error of any kind, return <error>.
*/
char *
login_getpath(login_cap_t *lc, const char *cap, char * error)
{
char *str = login_getcapstr(lc, (char*)cap, NULL, NULL);
if (str == NULL)
str = error;
else {
char *ptr = str;
while (*ptr) {
int count = strcspn(ptr, ", \t");
ptr += count;
if (*ptr)
*ptr++ = ':';
}
}
return str;
}
/*
* login_getcaptime()
* From the login_cap_t <lc>, get the capability <cap>, which is
* formatted as a time (e.g., "<cap>=10h3m2s"). If <cap> is not
* present in <lc>, return <def>; if there is an error of some kind,
* return <error>.
*/
rlim_t
login_getcaptime(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error)
{
char *res, *ep;
int ret;
rlim_t tot;
errno = 0;
if (lc == NULL || lc->lc_cap == NULL)
return def;
/*
* Look for <cap> in lc_cap.
* If it's not there (-1), return <def>.
* If there's an error, return <error>.
*/
if ((ret = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1)
return def;
else if (ret < 0)
return error;
/*
* "inf" and "infinity" are two special cases for this.
*/
if (!strcasecmp(res, "infinity") || !strcasecmp(res, "inf"))
return RLIM_INFINITY;
/*
* Now go through the string, turning something like 1h2m3s into
* an integral value. Whee.
*/
errno = 0;
tot = 0;
while (*res) {
rlim_t tim = STRTOV(res, &ep, 0);
if ((ep == NULL) || (ep == res) || errno) {
return error;
}
/* Look for suffixes */
switch (*ep++) {
case 0:
ep--; break; /* end of string */
case 's': case 'S': /* seconds */
break;
case 'm': case 'M': /* minutes */
tim *= 60L;
break;
case 'h': case 'H': /* hours */
tim *= (60L * 60L);
break;
case 'd': case 'D': /* days */
tim *= (60L * 60L * 24L);
break;
case 'w': case 'W': /* weeks */
tim *= (60L * 60L * 24L * 7L);
case 'y': case 'Y': /* Years */
/* I refuse to take leap years into account here. Sue me. */
tim *= (60L * 60L * 24L * 365L);
default:
return error;
}
res = ep;
tot += tim;
}
return tot;
}
/*
* login_getcapnum()
* From the login_cap_t <lc>, extract the numerical value <cap>.
* If it is not present, return <def> for a default, and return
* <error> if there is an error.
* Like login_getcaptime(), only it only converts to a number, not
* to a time; "infinity" and "inf" are 'special.'
*/
rlim_t
login_getcapnum(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error)
{
char *ep, *res;
int ret;
rlim_t val;
if (lc == NULL || lc->lc_cap == NULL)
return def;
/*
* For BSDI compatibility, try for the tag=<val> first
*/
if ((ret = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1) {
long lval;
/*
* String capability not present, so try for tag#<val> as numeric
*/
if ((ret = cgetnum(lc->lc_cap, (char *)cap, &lval)) == -1)
return def; /* Not there, so return default */
else if (ret < 0)
return error;
return (rlim_t)lval;
}
else if (ret < 0)
return error;
if (!strcasecmp(res, "infinity") || !strcasecmp(res, "inf"))
return RLIM_INFINITY;
errno = 0;
val = STRTOV(res, &ep, 0);
if ((ep == NULL) || (ep == res) || errno)
return error;
return val;
}
/*
* login_getcapsize()
* From the login_cap_t <lc>, extract the capability <cap>, which is
* formatted as a size (e.g., "<cap>=10M"); it can also be "infinity".
* If not present, return <def>, or <error> if there is an error of
* some sort.
*/
rlim_t
login_getcapsize(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error) {
char *ep, *res;
int ret;
rlim_t tot, mult;
if (lc == NULL || lc->lc_cap == NULL)
return def;
if ((ret = cgetstr(lc->lc_cap, (char *)cap, &res)) == -1)
return def;
else if (ret < 0)
return error;
errno = 0;
tot = 0;
while (*res) {
rlim_t val = STRTOV(res, &ep, 0);
if ((res == NULL) || (res == ep) || errno)
return error;
switch (*ep++) {
case 0: /* end of string */
ep--;
mult = 1;
break;
case 'b': case 'B': /* 512-byte blocks */
mult = 512; break;
case 'k': case 'K': /* 1024-byte Kilobytes */
mult = 1024; break;
case 'm': case 'M': /* 1024-k kbytes */
mult = 1024 * 1024; break;
case 'g': case 'G': /* 1Gbyte */
mult = 1024 * 1024 * 1024; break;
#ifndef RLIM_LONG
case 't': case 'T': /* 1TBte */
mult = 1024LL * 1024LL * 1024LL * 1024LL; break;
#endif
default:
return error;
}
res = ep;
tot += (val * mult);
}
return tot;
}
/*
* login_getcapbool()
* From the login_cap_t <lc>, check for the existance of the capability
* of <cap>. Return <def> if <lc>->lc_cap is NULL, otherwise return
* the whether or not <cap> exists there.
*/
int
login_getcapbool(login_cap_t *lc, const char *cap, int def)
{
if (lc == NULL || lc->lc_cap == NULL)
return def;
return (cgetcap(lc->lc_cap, (char *)cap, ':') != NULL);
}
/*
* login_getstyle()
* Given a login_cap entry <lc>, and optionally a type of auth <auth>,
* and optionally a style <style>, find the style that best suits these
* rules:
* 1. If <auth> is non-null, look for an "auth-<auth>=" string
* in the capability; if not present, default to "auth=".
* 2. If there is no auth list found from (1), default to
* "passwd" as an authorization list.
* 3. If <style> is non-null, look for <style> in the list of
* authorization methods found from (2); if <style> is NULL, default
* to LOGIN_DEFSTYLE ("passwd").
* 4. If the chosen style is found in the chosen list of authorization
* methods, return that; otherwise, return NULL.
* E.g.:
* login_getstyle(lc, NULL, "ftp");
* login_getstyle(lc, "login", NULL);
* login_getstyle(lc, "skey", "network");
*/
char *
login_getstyle(login_cap_t *lc, char *style, const char *auth)
{
int i;
char **authtypes = NULL;
char *auths= NULL;
char realauth[64];
static char *defauthtypes[] = { LOGIN_DEFSTYLE, NULL };
if (auth != NULL && *auth != '\0' &&
snprintf(realauth, sizeof realauth, "auth-%s", auth) < sizeof realauth)
authtypes = login_getcaplist(lc, realauth, NULL);
if (authtypes == NULL)
authtypes = login_getcaplist(lc, "auth", NULL);
if (authtypes == NULL)
authtypes = defauthtypes;
/*
* We have at least one authtype now; auths is a comma-seperated
* (or space-separated) list of authentication types. We have to
* convert from this to an array of char*'s; authtypes then gets this.
*/
i = 0;
if (style != NULL && *style != '\0') {
while (authtypes[i] != NULL && strcmp(style, authtypes[i]) != 0)
i++;
}
lc->lc_style = NULL;
if (authtypes[i] != NULL && (auths = strdup(authtypes[i])) != NULL)
lc->lc_style = auths;
return lc->lc_style;
}