diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libutil/Makefile | 20 | ||||
-rw-r--r-- | lib/libutil/login_auth.3 | 71 | ||||
-rw-r--r-- | lib/libutil/login_auth.c | 383 | ||||
-rw-r--r-- | lib/libutil/login_cap.3 | 378 | ||||
-rw-r--r-- | lib/libutil/login_cap.c | 564 | ||||
-rw-r--r-- | lib/libutil/login_class.3 | 187 | ||||
-rw-r--r-- | lib/libutil/login_class.c | 371 | ||||
-rw-r--r-- | lib/libutil/login_ok.3 | 109 | ||||
-rw-r--r-- | lib/libutil/login_ok.c | 242 | ||||
-rw-r--r-- | lib/libutil/login_times.3 | 155 | ||||
-rw-r--r-- | lib/libutil/login_times.c | 166 |
11 files changed, 2644 insertions, 2 deletions
diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile index e5f7063..2e6b8c8 100644 --- a/lib/libutil/Makefile +++ b/lib/libutil/Makefile @@ -4,9 +4,25 @@ LIB= util SHLIB_MAJOR= 2 SHLIB_MINOR= 1 CFLAGS+=-DLIBC_SCCS -I${.CURDIR} -I/sys -SRCS= login.c login_tty.c logout.c logwtmp.c pty.c setproctitle.c -MAN3+= login.3 login_tty.3 logout.3 logwtmp.3 pty.3 setproctitle.3 +SRCS= login.c login_tty.c logout.c logwtmp.c pty.c setproctitle.c \ + login_cap.c login_class.c login_auth.c login_times.c login_ok.c +MAN3+= login.3 login_tty.3 logout.3 logwtmp.3 pty.3 setproctitle.3 \ + login_cap.3 login_class.3 login_times.3 login_ok.3 +MAN5+= login.conf.5 MLINKS+= pty.3 openpty.3 pty.3 forkpty.3 +MLINKS+=login_cap.3 login_getclassbyname.3 login_cap.3 login_close.3 \ + login_cap.3 login_getclass.3 login_cap.3 login_getuserclass.3 \ + login_cap.3 login_getcapstr.3 login_cap.3 login_getcaplist.3 \ + login_cap.3 login_getstyle.3 login_cap.3 login_getcaptime.3 \ + login_cap.3 login_getcapnum.3 login_cap.3 login_getcapsize.3 \ + login_cap.3 login_getcapbool.3 login_cap.3 login_getpath.3 +MLINKS+=login_class.3 setusercontext.3 login_class.3 setclasscontext.3 \ + login_class.3 setclassenvironment.3 login_class.3 setclassresources.3 +MLINKS+=login_times.3 parse_lt.3 login_times.3 in_ltm.3 \ + login_times.3 in_lt.3 login_times.3 in_ltms.3 \ + login_times.3 in_lts.3 +MLINKS+=login_ok.3 auth_ttyok.3 login_ok.3 auth_hostok.3 \ + login_ok.3 auth_timeok.3 beforeinstall: ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/libutil.h \ diff --git a/lib/libutil/login_auth.3 b/lib/libutil/login_auth.3 new file mode 100644 index 0000000..555195f --- /dev/null +++ b/lib/libutil/login_auth.3 @@ -0,0 +1,71 @@ +.\" Copyright (c) 1995 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 author +.\" David Nugent. +.\" 5. Modifications may be freely made to this file providing the above +.\" conditions are met. +.\" +.\" $Id$ +.\" +.Dd December 29, 1996 +.Os FreeBSD +.Dt LOGIN_AUTH 3 +.Sh NAME +.Nm authenticate +.Nm auth_script +.Nm auth_env +.Nm auth_scan +.Nm auth_rmfiles +.Nm auth_checknologin +.Nm auth_cat +.Nm auth_ttyok +.Nm auth_hostok +.Nm auth_timesok +.Nd Authentication style support library for login class capabilities database. +.Sh SYNOPSIS +.Fd #include <sys/types.h> +.Fd #include <login_cap.h> +.Ft int +.Fn authenticate "const char *name" "const char *classname" "const char *style" "const char *service" +.Ft int +.Fn auth_script "const char * path" ... +.Ft int +.Fn auth_env "void" +.Ft int +.Fn auth_scan "int ok" +.Ft int +.Fn auth_rmfiles "void" +.Ft int +.Fn auth_checknologin "login_cap_t *lc" +.Ft int +.Fn auth_cat "const char *file" +.Ft int +.Fn auth_ttyok "login_cap_t *lc" "const char *tty" +.Ft int +.Fn auth_hostok "login_cap_t *lc" "const char *hostname" "char const *ip" +.Ft int +.Fn auth_timesok "login_cap_t *lc" "time_t now" +.Sh DESCRIPTION +This set of functions support the login class authorisation style interface provided +by +.Xr login.conf 5 . + +.Sh RETURN VALUES +.Sh SEE ALSO +.Xr login_cap 3 , +.Xr login_class 3 , +.Xr login.conf 5 , +.Xr termcap 5 , +.Xr getcap 3 diff --git a/lib/libutil/login_auth.c b/lib/libutil/login_auth.c new file mode 100644 index 0000000..38007b0 --- /dev/null +++ b/lib/libutil/login_auth.c @@ -0,0 +1,383 @@ +/*- + * 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 + * + * $Id$ + */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <pwd.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <login_cap.h> +#include <stdarg.h> +#include <paths.h> +#include <sys/wait.h> + +extern char *fgetline(FILE *, int*); + +#ifdef RLIM_LONG +# define STRTOV strtol +#else +# define STRTOV strtoq +#endif + +#define AUTHMAXLINES 1024 +#define AUTHMAXARGS 16 + +struct auth_info { + int reject; + int auths; + int env_count; + char **env; + int file_count; + char **files; +}; + +static struct auth_info auth_info; + +/* + * free_auth_info() + * Go through the auth_info structure, and free() anything of interest. + * This includes the string arrays, and any individual element. + * All part of being environmentally conscious ;). + */ + +static void +free_auth_info(void) +{ + int i; + char *ptr; + + auth_info.reject = 0; + auth_info.auths = 0; + if (auth_info.env) { + for (i = 0; i < auth_info.env_count; i++) { + if (auth_info.env[i]) + free(auth_info.env[i]); + } + free(auth_info.env); + auth_info.env = NULL; + } + if (auth_info.files) { + for (i = 0; i < auth_info.file_count; i++) { + if (auth_info.files[i]) + free(auth_info.files[i]); + } + free(auth_info.files); + auth_info.files = NULL; + } +} + + +/* + * collect_info() + * Read from <fd>, a list of authorization commands. + * These commands are: + * reject + * authorize [root|secure] + * setenv <name>[ <value>] + * remove <file> + * A single reject means the entire thing is bad; + * multiple authorize statements can be present (it would be + * silly, but that's what the spec says). + * The commands are collected, and are accted upon by: + * auth_scan() -- check for authorization or rejection + * auth_rmfiles() -- remove the specified files + * auth_env() -- set the specified environment variables + * We only get up to AUTHMAXLINES lines of input from the program. + */ +#define STRSIZEOF(x) (sizeof(x)-1) +static void +collect_info(int fd) +{ + char *line; + FILE *fp; + char *ptr; + int len; + int line_count = 0; + + fp = fdopen(fd, "r"); + + while ((line = fgetline(fp, &len)) != NULL) { + if (++line_count > AUTHMAXLINES) + break; + if (strncasecmp(line, BI_REJECT, STRSIZEOF(BI_REJECT)) == 0) { + auth_info.reject = 1; + } else if (strncasecmp(line, BI_AUTH, STRSIZEOF(BI_AUTH)) == 0) { + ptr = line + STRSIZEOF(BI_AUTH); + ptr += strspn(ptr, " \t"); + if (!*ptr) + auth_info.auths |= AUTH_OKAY; + else if (strncasecmp(ptr, BI_ROOTOKAY, STRSIZEOF(BI_ROOTOKAY)) == 0) + auth_info.auths |= AUTH_ROOTOKAY; + else if (strncasecmp(ptr, BI_SECURE, STRSIZEOF(BI_SECURE)) == 0) + auth_info.auths |= AUTH_SECURE; + } else if (strncasecmp(line, BI_SETENV, STRSIZEOF(BI_SETENV)) == 0) { + ptr = line + STRSIZEOF(BI_SETENV); + ptr += strspn(ptr, " \t"); + if (*ptr) { + char **tmp = realloc(auth_info.env, sizeof(char*) * (auth_info.env_count + 1)); + if (tmp != NULL) { + auth_info.env = tmp; + if ((auth_info.env[auth_info.env_count] = strdup(ptr)) != NULL) + auth_info.env_count++; + } + } + } else if (strncasecmp(line, BI_REMOVE, STRSIZE(BI_REMOVE)) == 0) { + ptr = line + STRSIZE(BI_REMOVE); + ptr += strspn(ptr, " \t"); + if (*ptr) { + char **tmp = realloc(auth_info.files, sizeof(char*) * (auth_info.file_count + 1)); + if (tmp != NULL) { + auth_info.files = tmp; + if ((auth_info.files[auth_info.file_count] = strdup(ptr)) != NULL) + auth_info.file_count++; + } + } + } + } + fclose(fp); +} + + +/* + * authenticate() + * Starts an auth_script() for the given <user>, with a class <class>, + * style <style>, and service <service>. <style> is necessary, + * as are <user> and <class>, but <service> is optional -- it defaults + * to "login". + * Since auth_script() expects an execl'able program name, authenticate() + * also concatenates <style> to _PATH_AUTHPROG. + * Lastly, calls auth_scan(AUTH_NONE) to see if there are any "reject" statements, + * or lack of "auth" statements. + * Returns -1 on error, 0 on rejection, and >0 on success. + * (See AUTH_* for the return values.) + * + */ +int +authenticate(const char * name, const char * class, const char * style, const char *service) +{ + int retval; + + if (style == NULL || *style == '\0') + retval = -1; + else { + char buf[sizeof(_PATH_AUTHPROG) + 64]; + + if (service == NULL || *service == '\0') + service = LOGIN_DEFSERVICE; + + free_auth_info(); + + if (snprintf(buf, sizeof buf, _PATH_AUTHPROG "%s", style) >= sizeof buf) + retval = -1; + else { + retval = auth_script(buf, style, "-s", service, name, class, NULL); + if (retval >= 0) + retval = auth_scan(AUTH_NONE); + } + } + return retval; +} + + +/* + * auth_script() + * Runs an authentication program with specified arguments. + * It sets up file descriptor 3 for the program to write to; + * it stashes the output somewhere. The output of the program + * consists of statements: + * reject + * authorize [root|secure] + * setenv <name> [<value>] + * remove <file> + * + * Terribly exciting, isn't it? There is no limit specified in + * BSDi's API for how much output can be present, but we should + * keep it fairly small, I think. + * No more than AUTHMAXLINES lines. + */ + +int +auth_script(const char * path, ...) +{ + va_list ap; + int pid, status; + int argc = 0; + int p[2]; /* pipes */ + char *argv[AUTHMAXARGS+1]; + + va_start(ap, path); + while (argc < AUTHMAXARGS && (argv[argc++] = va_arg(ap, char*)) != NULL) + ; + argv[argc] = NULL; + va_end(ap); + + fflush(NULL); + + if (pipe(p) >= 0) { + if ((pid = fork()) == -1) { + close(p[0]); + close(p[1]); + } else if (pid == 0) { /* Child */ + close(p[0]); + dup2(p[1], 3); + if (setenv("PATH", _PATH_DEFPATH, 1)==0 && setenv("SHELL", _PATH_BSHELL, 1)==0) + execv(path, argv); + _exit(1); + } else { + close(p[1]); + collect_info(p[0]); + if (waitpid(pid, &status, 0) != -1 && WIFEXITED(status) && !WEXITSTATUS(status)) + return 0; + } + } + return -1; +} + + +/* + * auth_env() + * Processes the stored "setenv" lines from the stored authentication + * output. + */ + +int +auth_env(void) +{ + int i; + + for (i = 0; i < auth_info.env_count; i++) { + char *nam = auth_info.env[i]; + char *ptr = nam + strcspn(nam, " \t="); + if (*ptr) { + *ptr++ = '\0'; + ptr += strspn(ptr, " \t"); + } + setenv(nam, ptr, 1); + } +} + + +/* + * auth_scan() + * Goes through the output of the auth_script/authenticate, and + * checks for a failure or authentication. + * <ok> is a default authentication value -- if there are no + * rejection or authentication statements, then it is returned + * unmodified. + * AUTH_NONE is returned if there were any reject statements + * from the authentication program (invoked by auth_script()), and + * AUTH, AUTH_ROOTOKAY, and/or AUTH_SECURE are returned if the + * appropriate directives were found. Note that AUTH* are + * *bitmasks*! + */ + +int +auth_scan(int ok) +{ + if (auth_info.reject) + return 0; + return ok | auth_info.auths; +} + + +/* + * auth_rmfiles() + * Removes any files that the authentication program said needed to be + * removed, said files having come from a previous execution of + * auth_script(). + */ + +int +auth_rmfiles(void) +{ + int i = auth_info.file_count; + while (i-- > 0) { + unlink(auth_info.files[i]); + free(auth_info.files[i]); + auth_info.files[i] = NULL; + } +} + + +/* + * auth_checknologin() + * Checks for the existance of a nologin file in the login_cap + * capability <lc>. If there isn't one specified, then it checks + * to see if this class should just ignore nologin files. Lastly, + * it tries to print out the default nologin file, and, if such + * exists, it exits. + */ + +void +auth_checknologin(login_cap_t *lc) +{ + char *file; + + /* Do we ignore a nologin file? */ + if (login_getcapbool(lc, "ignorenologin", 0)) + return; + + /* Note that <file> will be "" if there is no nologin capability */ + if ((file = login_getcapstr(lc, "nologin", "", NULL)) == NULL) + exit(1); + + /* + * *file is true IFF there was a "nologin" capability + * Note that auth_cat() returns 1 only if the specified + * file exists, and is readable. E.g., /.nologin exists. + */ + if ((*file && auth_cat(file)) || auth_cat(_PATH_NOLOGIN)) + exit(1); +} + + +/* + * auth_cat() + * Checks for the readability of <file>; if it can be opened for + * reading, it prints it out to stdout, and then exits. Otherwise, + * it returns 0 (meaning no nologin file). + */ +int +auth_cat(const char *file) +{ + int fd, count; + char buf[BUFSIZ]; + + if ((fd = open(file, O_RDONLY)) < 0) + return 0; + while ((count = read(fd, buf, sizeof(buf))) > 0) + write(fileno(stdout), buf, count); + close(fd); + return 1; +} diff --git a/lib/libutil/login_cap.3 b/lib/libutil/login_cap.3 new file mode 100644 index 0000000..3fbf3a6 --- /dev/null +++ b/lib/libutil/login_cap.3 @@ -0,0 +1,378 @@ +.\" Copyright (c) 1995 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 author +.\" David Nugent. +.\" 5. Modifications may be freely made to this file providing the above +.\" conditions are met. +.\" +.\" $Id$ +.\" +.Dd December 27, 1996 +.Os FreeBSD +.Dt LOGIN_CAP 3 +.Sh NAME +.Nm login_getclassbyname , +.Nm login_close , +.Nm login_getclass , +.Nm login_getuserclass , +.Nm login_getcapstr , +.Nm login_getcaplist , +.Nm login_getcaptime , +.Nm login_getcapnum , +.Nm login_getcapsize , +.Nm login_getcapbool , +.Nm login_getstyle +.Nd functions for accessing the login class capabilities database. +.Sh SYNOPSIS +.Fd #include <sys/types.h> +.Fd #include <login_cap.h> +.Ft void +.Fn login_close "login_cap_t * lc" +.Ft login_cap_t * +.Fn login_getclassbyname "const char *nam" "const char *dir" +.Ft login_cap_t * +.Fn login_getclass "const struct passwd *pwd" +.Ft login_cap_t * +.Fn login_getuserclass "const struct passwd *pwd" +.Ft char * +.Fn login_getcapstr "login_cap_t *lc" "const char *cap" "char *def" "char *error" +.Ft char ** +.Fn login_getcaplist "login_cap_t *lc" "const char *cap" "const char *chars" +.Ft char * +.Fn login_getpath "login_cap_t *lc" "const char *cap" "char *error" +.Ft rlim_t +.Fn login_getcaptime "login_cap_t *lc" "const char *cap" "rlim_t def" "rlim_t error" +.Ft rlim_t +.Fn login_getcapnum "login_cap_t *lc" "const char *cap" "rlim_t def" "rlim_t error" +.Ft rlim_t +.Fn login_getcapsize "login_cap_t *lc" const "char *cap" "rlim_t def" "rlim_t error" +.Ft int +.Fn login_getcapbool "login_cap_t *lc" "const char *cap" "int def" +.Ft char * +.Fn login_getstyle "login_cap_t *lc" "char *style" "const char *auth" +.Pp +.Sh DESCRIPTION +These functions represent a programming interface to the login +classes database provided in +.Xr login.conf 5 . +This database contains capabilities, attributes and default environment +and accounting settings for users and programs running as specific users, +as determined by the login class field within entries in +.Pa /etc/master.passwd . +.Pp +Entries in +.Xr login.conf 5 +consist of colon +.Ql \&: +separated fields, the first field in each record being one or more +identifiers for the record which must be unique for the entire database +each separated by a '|' and may optionally include a description as +the last 'name'. +Remaining fields in the record consist of keyword/data pairs. +Long lines may be continued with a backslash within empty entries +with the second and subsequent lines optionally indented for readability. +This is similar to the format used in +.Xr termcap 5 +except that keywords are not limited to two significant characters, +and are usually longer for improved readability. +As with termcap entries, multiple records can be linked together +(one record including another) using a field containing tc=<recordid>, +the result is that the entire record referenced by <recordid> replaces +the tc= field at the point at which it occurs. +See +.Xr getcap 3 +for further details on the format and use of a capabilities database. +.Pp +The +.Nm login_cap +interface provides a convenient means of retrieving login class +records with all tc= references expanded. +A program will typically call one of +.Fn login_getclass , +.Fn login_getuserclass +or +.Fn login_getclassbyname +according to its requirements. +Each of these functions returns a login capabilities structure, +.Ft login_cap_t +which may subsequently be used to interrogate the database for +specific values using the rest of the API. +Once the login_cap_t is of no further use, the +.Fn login_close +function should be called to free all resources used. +.Pp +The structure of login_cap_t is defined in login_cap.h, as: +.Bd -literal -offset indent +typedef struct { + char *lc_class; + char *lc_cap; + char *lc_style; +} login_cap_t; +.Ed +.Pp +The +.Ar lc_class +member contains a pointer to the name of the login class +retrieved. +This may not necessarily be the same as the one requested, +either directly via +.Fn login_getclassbyname , +or indirectly via a user's login record using +.Fn login_getclass +or +.Fn login_getuserclass . +If the referenced user has no login class specified in +.Pa /etc/master.passwd , +the class name is NULL or an empty string, or if the class +specified does not exist in the database, each of these +functions will search for a record with an id of "default", +with that name returned in the +.Ar lc_class +field. +.Pp +The +.Ar lc_cap +field is used internally by the library to contain the +expanded login capabilities record. +Programs with unusual requirements may wish to use this +with the lower-level +.Fn getcap +style functions to access the record directly. +.Pp +The +.Ar lc_style +field is set by the +.Fn login_getstyle +function to the authorisation style according to the requirements +of the program handling a login itself. +.Pp +As noted above, the +.Fn get*class +functions return a login_cap_t object which is used to access +the matching or default record in the capabilities database. +.Fn getclassbyname +accepts two arguments: the first one is the record identifier of the +record to be retrieved, the second being an optional directory name. +If the first +.Ar name +argument is NULL, an empty string, or a class that does not exist +in the supplimental or system login class database, then the system +.Em default +record is returned instead. +If the second +.Ar dir +parameter is NULL, then only the system login class database is +used, but when not NULL, the named directory is searched for +a login database file called ".login.conf", and capability records +contained within it may override the system defaults. +This scheme allows users to override some login settings from +those in the system login class database by creating class records +for their own private class with a record id of `me'. +In the context of a +.Em login , +it should be noted that some options cannot by overridden by +users for two reasons; many options, such as resource settings +and deafult process priorities, require root privileges +in order to take effect, and other fields in the user's file are +not be consulted at all during the early phases of login for +security or administrative reasons. +See +.Xr login.conf 5 +for more information on which settings a user is able to override. +Typically, these are limited purely to the user's default login +environment which might otherwise have been overridden in shell +startup scripts in any case. +The user's +.Pa .login.conf +merely provides a convenient way for a user to set up their preferred +login environment before the shell is invoked on login. +.Pp +If the specified record is NULL, empty or does not exist, and the +system has no "default" record available to fallback, there is a +memory allocation error or for some reason +.Xr cgetent 3 +is unable to access the login capabilities database, this function +returns NULL. +.Pp +The functions +.Fn login_getclass +and +.Fn login_getuserclass +retrieve the applicable login class record for the user's passwd +entry by calling +.Fn login_getclassbyname . +On failure, NULL is returned. +The difference between the two functions is that +.Fn login_getuserclass +includes the user's overriding +.Pa .login.conf +that exists in the user's home directory, while +.Fn login_getclass +restricts itself only to the system login class database in +.Pa /etc/login.conf . +In either case, if the passwd pointer is NULL, or the user has +no class, then the system "default" entry is retrieved. +.Pp +Once a program no longer wishes to use a login_cap_t object, +.Fn login_close +may be called to free all resources used by the login class. +.Fn login_close +may be passed a NULL pointer with no harmful side-effects. +.Pp +The remaining functions may be used to retrieve individual +capability records. +Each function takes a login_cap_t object as its first parameter, +a capability tag as the second, and remaining parameters being +default and error values that are returned if the capability is +not found. +The type of the additional parameters passed and returned depend +on the +.Em type +of capability each deals with, be it a simple string, a list, +a time value, a file or memory size value, a path (consisting of +a colon-separated list of directories) or a boolean flag. +The manpage for +.Xr login.conf 5 +deals in specific tags and their type. +.Pp +Note that with all functions in this group, you should not call +.Xr free 3 +on any pointers returned. +Memory allocated during retrieval or processing of capability +tags is automatically reused by subsequent calls to functions +in this group, or deallocated on calling +.Fn login_close . +.Bl -tag -width "login_getcaplist()" +.It Fn login_getcapstr +This function returns a simple string capability. +If the string is not found, then the value in +.Ar def +is returned as the default value, or if an error +occurs, the value in the +.Ar error +parameter is returned. +.It Fn login_getcaplist +This function returns the value corresponding to the named +capability tag as a list of values in a NULL terminated +array. +Within the login class database, some tags are of type +.Em list , +which consist of one or more comma- or space separated +values. +Usually, this function is not called directly from an +application, but is used indirectly via +.Fn login_getstyle . +.It Fn login_getpath +This function returns a list of directories separated by colons +.Ql &: . +Capability tags for which this function is called consist of a list of +directories separated by spaces. +.It Fn login_getcaptime +This function returns a +.Em time value +associated with a particular capability tag with the value expressed +in seconds (the default), minutes, hours, days, weeks or (365 day) +years or any combination of these. +A suffix determines the units used: S for seconds, M for minutes, +H for hours, D for days, W for weeks and Y for 365 day years. +Case of the units suffix is ignored. +.Pp +Time values are normally used for setting resource, accounting and +session limits. +If supported by the operating system and compiler (which is true of +FreeBSD), the value returned is a quad (long long), of type +.Em rlim_t . +A value "inf" or "infinity" may be used to express an infinite +value, in which case RLIM_INFINITY is returned. +.It Fn login_getcapnum +This function returns a numeric value for a tag, expressed either as +tag=<value> or the standard +.Fn cgetnum +format tag#<value>. +The first format should be used in preference to the second, the +second format is provided for compatibility and consistency with the +.Xr getcap 3 +database format where numeric types use the +.Ql \&# +as the delimiter for numeric values. +If in the first format, then the value given may be "inf" or +"infinity" which results in a return value of RLIM_INFINITY. +If the given capability tag cannot be found, the +.Ar def +parameter is returned, and if an error occurs, the +.Ar error +parameter is returned. +.It Fn login_getcapsize +.Fn login_getcapsize +returns a value representing a size (typicially, file or memory) +which may be expressed as bytes (the default), 512 byte blocks, +kilobytes, megabytes, gigabytes, and on systems that support the +.Ar long long +type, terrabytes. +The suffix used determines the units, and multiple values and +units may be used in combination (e.g. 1m500k = 1.5 megabytes). +A value with no suffix is interpreted as bytes, B as 512-byte +blocks, K as kilobytes, M as megabytes, G as gigabytes and T as +terrabytes. +Case is ignored. +The error value is returned if there is a login capabilities database +error, if an invalid suffix is used, or if a numeric value cannot be +interpreted. +.It Fn login_getcapbool +This function returns a boolean value tied to a particular flag. +It returns 0 if the given capability tag is not present or is +negated by the presence of a "tag@" (See +.Xr getcap 3 +for more information on boolean flags), and returns 1 if the tag +is found. +.It Fn login_getstyle +This function is used by the login authorisation system to determine +the style of login available in a particular case. +The function accepts three parameters, the login_cap entry itself and +two optional parameters, and authorisation type 'auth' and 'style', and +applies these to determine the authorisation style that best suites +these rules. +.Bl -bullet -indent offset +.It +If 'auth' is neither NULL nor an empty string, look for a tag of type +"auth-<auth>" in the capability record. +If not present, then look for the default default tag "auth=". +.It +If no valid authorisation list was found from the previous step, then +default to "passwd" as the authorisation list. +.It +If 'style' is not NULL or empty, look for it in the list of authorisation +methods found from the pprevious step. +If 'style' is NULL or an empty string, then default to "passwd" +authorisation. +.It +If 'style' is found in the chosen list of authorisation methods, then +return that, otherwise return NULL. +.El +.Pp +This scheme allows the administrator to determine the types of +authorisation methods accepted by the system, depending on the +means by which the access occurs. +For example, the administrator may require skey or kerberos as +the authentication method used for access to the system via the +network, and standard methods via direct dialup or console +logins, significantly reducing the risk of password discovery +by "snooping" network packets. +.El +.Sh SEE ALSO +.Xr login_class 3 , +.Xr login.conf 5 , +.Xr termcap 5 , +.Xr getcap 3 diff --git a/lib/libutil/login_cap.c b/lib/libutil/login_cap.c new file mode 100644 index 0000000..21ff02f1 --- /dev/null +++ b/lib/libutil/login_cap.c @@ -0,0 +1,564 @@ +/*- + * 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 + * + * $Id$ + */ + +#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 = internal_string; + 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 = ptr + count + 1; + } + + 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; +} + +static void +freearraystr(char ** array) +{ + /* + * the array[0] should be free'd, and then array. + */ + if (array) { + free(array[0]); + free(array); + } +} + + +/* + * 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_string = NULL; + 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 = (pwd == NULL) ? NULL : pwd->pw_class; + if (pwd->pw_class == NULL || *pwd->pw_class == '\0') + class = (pwd->pw_uid == 0) ? "root" : NULL; /* Kludge for 'root' user(s) */ + 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 *ptr, *str = login_getcapstr(lc, (char*)cap, NULL, NULL); + + if (str == NULL || (ptr = allocstr(str)) == NULL) + str = error; + else { + 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 = 0, tim; + + 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; + while (*res) { + 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 val; + rlim_t 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; + val = STRTOV(res, &ep, 0); + if ((res == NULL) || (res == ep) || errno) + return error; + switch (*ep) { + case 0: /* end of string */ + 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; + } + return val * mult; +} + + +/* + * 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; +} + + diff --git a/lib/libutil/login_class.3 b/lib/libutil/login_class.3 new file mode 100644 index 0000000..370bfbf --- /dev/null +++ b/lib/libutil/login_class.3 @@ -0,0 +1,187 @@ +.\" Copyright (c) 1995 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 author +.\" David Nugent. +.\" 5. Modifications may be freely made to this file providing the above +.\" conditions are met. +.\" +.\" $Id$ +.\" +.Dd December 28, 1996 +.Os FreeBSD +.Dt LOGIN_CLASS 3 +.Sh NAME +.Nm setclasscontext , +.Nm setusercontext , +.Nm setclassresources , +.Nm setclassenvironment +.Nd functions for using the login class capabilities database. +.Sh SYNOPSIS +.Fd #include <sys/types.h> +.Fd #include <login_cap.h> +.Ft int +.Fn setclasscontext "char *classname" "unsigned int flags" +.Ft int +.Fn setusercontext "login_cap_t *lc" "const struct passwd *pwd" "uid_t uid" "unsigned int flags" +.Ft void +.Fn setclassresources "login_cap_t *lc" +.Ft void +.Fn setclassenvironment "login_cap_t *lc" "const struct passwd *pwd" "int paths" +.Pp +.Sh DESCRIPTION +These functions provide a higher level interface to the login class +database than those documented in +.Xr login_cap 3 . +These functions are used to set resource limits, environment and +accounting settings for users on logging into the system and when +selecting an appropriate set of environment and resource settings +for system daemons based on login classes. +These functions may only be called if the current process is +running with root priviledges. +If the LOGIN_SETLOGIN flag is used this function calls +.Xr setlogin 2 , +and due care must be taken as detailed in the manpage for that +function and this affects all processes running in the same session +and not just the current process. +.Pp +.Fn setclasscontext +sets various class context values (resource limits, umask and +process priorities) based on values for a specific named class. +.Pp +The function +.Fn setusercontext +sets class context values based on a given login_cap_t +object, a specific passwd record (if login_cap_t is NULL), +sets the current session's login and the current process +user and group ownership. +Each of these functions is selectable via bit-flags passed +in the +.Ar flags +parameter, which is comprised of one or more of the following: +.Bl -tag -width LOGIN_SETRESOURCES +.It LOGIN_SETLOGIN +Set the login associated with the current session to the user +specified in the passwd structure. +.Xr setlogin 2 . +The +.Ar pwd +parameter must not be NULL if this option is used. +.It LOGIN_SETUSER +Set ownship of the current process to the uid specified in the +.Ar uid +parameter using +.Xr setuid 2 . +.It LOGIN_SETGROUP +Set group ownership of the current process to the group id +specified in the passwd structure using +.Xr setgid 2 , +and calls +.Xr initgroups 3 +to set up the group access list for the current process. +The +.Ar pwd +parameter must not be NULL if this option is used. +.It LOGIN_SETRESOURCES +Set resource limits for the current process based on values +specified in the system login class database. +Class capability tags used, with and without -cur (soft limit) +or -max (hard limit) suffixes and the corresponding resource +setting: +.Bd -literal +cputime RLIMIT_CPU +filesize RLIMIT_FSIZE +datasize RLIMIT_DATA +stacksize RLIMIT_STACK +coredumpsize RLIMIT_CORE +memoryuse RLIMIT_RSS +memorylocked RLIMIT_MEMLOCK +maxproc RLIMIT_NPROC +openfiles RLIMIT_NOFILE +.Ed +.It LOGIN_SETPRIORITY +Set the scheduling priority for the current process based on the +value specified in the system login class database. +Class capability tags used: +.Bd -literal +priority +.Ed +.It LOGIN_SETUMASK +Set the umask for the current process to a value in the user or +system login class database. +Class capability tags used: +.Bd -literal +umask +.Ed +.It LOGIN_SETPATH +Set the "path" and "manpath" environment variables based on values +in the user or system login class database. +Class capability tags used with the corresponding environment +variables set: +.Bd -literal +path PATH +manpath MANPATH +.Ed +.It LOGIN_SETENV +Set various environment variables based on values in the user or +system login class database. +Class capability tags used with the corresponding environment +variables set: +.Bd -literal +lang LANG +charset MM_CHARSET +timezone TZ +term TERM +.Ed +.Pp +Additional environment variables may be set using the list type +capability "setenv=var1 val1,var2 val2..,varN valN". +.It LOGIN_SETALL +Enables all of the above settings. +.El +.Pp +Note that when setting environment variables and a valid passwd +pointer is provided in the +.Ar pwd +parameter, the characters +.Ql \&~ +and +.Ql \&$ +are substituted for the user's home directory and login name +respectively. +.Pp +The +.Fn setclassresources +and +.Fn setclassenvironment +functions are subsets of the setcontext functions above, but may +be useful in isolation. +.Sh RETURN VALUES +.Fn setclasscontext +and +.Fn setusercontext +return -1 if an error occured, or 0 on success. +If an error occurs when attempting to set the user, login, group +or resources, a message is reported to +.Xr syslog 3 , +with LOG_ERR priority and directed to the currently active facility. +.Sh SEE ALSO +.Xr setlogin 2 , +.Xr setuid 2 , +.Xr setgid 2 , +.Xr initgroups 3 , +.Xr login_cap 3 , +.Xr login.conf 5 , +.Xr termcap 5 , +.Xr getcap 3 diff --git a/lib/libutil/login_class.c b/lib/libutil/login_class.c new file mode 100644 index 0000000..1dc6cc4 --- /dev/null +++ b/lib/libutil/login_class.c @@ -0,0 +1,371 @@ +/*- + * 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. + * + * High-level routines relating to use of the user capabilities database + * + * $Id$ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <fcntl.h> +#include <pwd.h> +#include <syslog.h> +#include <login_cap.h> +#include <paths.h> + + +#undef UNKNOWN +#define UNKNOWN "su" + + +static struct login_res { + const char * what; + rlim_t (*who)(login_cap_t *, const char *, rlim_t, rlim_t); + int why; +} resources[] = { + { "cputime", login_getcaptime, RLIMIT_CPU }, + { "filesize", login_getcapsize, RLIMIT_FSIZE }, + { "datasize", login_getcapsize, RLIMIT_DATA }, + { "stacksize", login_getcapsize, RLIMIT_STACK }, + { "coredumpsize", login_getcapsize, RLIMIT_CORE }, + { "memoryuse", login_getcapsize, RLIMIT_RSS }, + { "memorylocked", login_getcapsize, RLIMIT_MEMLOCK }, + { "maxproc", login_getcapnum, RLIMIT_NPROC }, + { "openfiles", login_getcapnum, RLIMIT_NOFILE }, + { NULL, 0, 0 } +}; + + + +void +setclassresources(login_cap_t *lc) +{ + struct login_res *lr = resources; + + while (lr->what != NULL) { + struct rlimit rlim, + newlim; + char cur[40], + max[40]; + rlim_t rcur, + rmax; + + sprintf(cur, "%s-cur", lr->what); + sprintf(max, "%s-max", lr->what); + + /* + * The login.conf file can have <limit>, <limit>-max, and + * <limit>-cur entries. + * What we do is get the current current- and maximum- limits. + * Then, we try to get an entry for <limit> from the capability, + * using the current and max limits we just got as the + * default/error values. + * *Then*, we try looking for <limit>-cur and <limit>-max, + * again using the appropriate values as the default/error + * conditions. + */ + + getrlimit(lr->why, &rlim); + rcur = rlim.rlim_cur; + rmax = rlim.rlim_max; + + rcur = (*lr->who)(lc, lr->what, rcur, rcur); + rmax = (*lr->who)(lc, lr->what, rmax, rmax); + newlim.rlim_cur = (*lr->who)(lc, cur, rcur, rcur); + newlim.rlim_max = (*lr->who)(lc, max, rmax, rmax); + + if (setrlimit(lr->why, &newlim) == -1) + syslog(LOG_WARNING, "set class '%s' resource limit %s: %m", lc->lc_class, lr->what); + + ++lr; + } +} + +static struct login_vars { + const char * tag; + const char * var; + const char * def; +} pathvars[] = { + { "path", "PATH", NULL }, + { "manpath", "MANPATH", NULL }, + { NULL, NULL, NULL } +}, envars[] = { + { "lang", "LANG", NULL }, + { "charset", "MM_CHARSET", NULL }, + { "timezone", "TZ", NULL }, + { "term", "TERM", UNKNOWN }, + { NULL, NULL, NULL } +}; + +static char * +substvar(char * var, const struct passwd * pwd, int hlen, int pch, int nlen) +{ + char * np = NULL; + + if (var != NULL) { + int tildes = 0; + int dollas = 0; + char * p; + + if (pwd != NULL) { + /* + * Count the number of ~'s in var to substitute + */ + p = var; + while ((p = strchr(p, '~')) != NULL) { + ++tildes; + ++p; + } + + /* + * Count the number of $'s in var to substitute + */ + p = var; + while ((p = strchr(p, '$')) != NULL) { + ++dollas; + ++p; + } + } + + np = malloc(strlen(var) + (dollas * nlen) - dollas + (tildes * (pch+hlen)) - tildes + 1); + + if (np != NULL) { + p = strcpy(np, var); + + if (pwd != NULL) { + /* + * This loop does user username and homedir substitutions + * for unescaped $ (username) and ~ (homedir) + */ + while (*(p += strcspn(p, "~$")) != '\0') { + int l = strlen(p); + + if (p > var && *(p-1) == '\\') /* Escaped: */ + memmove(p - 1, p, l + 1); /* Slide-out the backslash */ + else if (*p == '~') { + memmove(p + 1, p + hlen + pch, l); /* Subst homedir */ + memmove(p, pwd->pw_dir, hlen); + if (pch) + p[hlen] = '/'; + p += hlen + pch; + } + else /* if (*p == '$') */ { + memmove(p + 1, p + nlen, l); /* Subst username */ + memmove(p, pwd->pw_name, nlen); + p += nlen; + } + } + } + } + } + return np; +} + + +void +setclassenvironment(login_cap_t *lc, const struct passwd * pwd, int paths) +{ + struct login_vars * vars = paths ? pathvars : envars; + int hlen = pwd ? strlen(pwd->pw_dir) : 0; + int nlen = pwd ? strlen(pwd->pw_name) : 0; + char pch = 0; + + if (hlen && pwd->pw_dir[hlen-1] != '/') + ++pch; + + while (vars->tag != NULL) { + char * var = paths ? login_getpath(lc, vars->tag, NULL) + : login_getcapstr(lc, vars->tag, NULL, NULL); + + char * np = substvar(var, pwd, hlen, pch, nlen); + + if (np != NULL) { + setenv(vars->var, np, 1); + free(np); + } else if (vars->def != NULL) { + setenv(vars->var, vars->def, 0); + } + ++vars; + } + + /* + * If we're not processing paths, then see if there is a setenv list by + * which the admin and/or user may set an arbitrary set of env vars. + */ + if (!paths) { + char ** set_env = login_getcaplist(lc, "setenv", ","); + if (set_env != NULL) { + while (*set_env != NULL) { + char *p = strchr(*set_env, '='); + if (p != NULL) { + char * np = substvar(set_env[1], pwd, hlen, pch, nlen); + if (np != NULL) { + setenv(set_env[0], np, 1); + free(np); + } + } + ++set_env; + } + } + } + +} + + +/* + * setclasscontext() + * + * For the login class <class>, set various class context values + * (limits, mainly) to the values for that class. Which values are + * set are controlled by <flags> -- see <login_class.h> for the + * possible values. + * + * setclasscontext() can only set resources, priority, and umask. + */ + +int +setclasscontext(const char *classname, unsigned int flags) +{ + int rc; + login_cap_t * lc = login_getclassbyname(classname, NULL); + flags &= (LOGIN_SETRESOURCES| LOGIN_SETPRIORITY|LOGIN_SETUMASK); + rc = setusercontext(lc, NULL, 0, flags); + login_close(lc); + return rc; +} + + +/* + * setusercontext() + * + * Given a login class <lc> and a user in <pwd>, with a uid <uid>, + * set the context as in setclasscontext(). <flags> controls which + * values are set. + * + * The difference between setclasscontext() and setusercontext() is + * that the former sets things up for an already-existing process, + * while the latter sets things up from a root context. Such as might + * be called from login(1). + * + */ + +int +setusercontext(login_cap_t *lc, const struct passwd *pwd, uid_t uid, unsigned int flags) +{ + int i; + login_cap_t * llc = NULL; + + if (lc == NULL) + { + if (pwd == NULL || (lc = login_getclass(pwd)) == NULL) { + return -1; + } + llc = lc; /* free this when we're done */ + } + + if (flags & LOGIN_SETPATH) + pathvars[0].def = uid ? _PATH_DEFPATH : _PATH_STDPATH; + + /* + * Set the process priority + */ + if (flags & LOGIN_SETPRIORITY) { + int pri = (int)login_getcapnum(lc, "priority", LOGIN_DEFPRI, LOGIN_DEFPRI); + pri = (pri < PRIO_MIN) ? PRIO_MIN : (pri > PRIO_MAX) ? PRIO_MAX : pri; + if (setpriority(PRIO_PROCESS, 0, pri) != 0) + syslog(LOG_WARNING, "setpriority '%s': %m", pwd->pw_name); + } + + /* + * Set resources + */ + if (flags & LOGIN_SETRESOURCES) + setclassresources(lc); + + /* + * Set the sessions login + */ + if ((flags & LOGIN_SETLOGIN) && setlogin(pwd->pw_name) != 0) { + syslog(LOG_ERR, "setlogin '%s': %m", pwd->pw_name); + login_close(llc); + return -1; /* You can't be too paranoid */ + } + + /* + * Setup the user's group permissions + */ + if (flags & LOGIN_SETGROUP) { + /* XXX is it really a good idea to let errors here go? */ + if (setgid(pwd->pw_gid) != 0) + syslog(LOG_WARNING, "setgid %ld: %m", (long)pwd->pw_gid); + if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) + syslog(LOG_WARNING, "initgroups '%s': %m", pwd->pw_name); + } + + /* + * This needs to be done after all of the above. + */ + if ((flags & LOGIN_SETUSER) && setuid(uid) != 0) { + syslog(LOG_ERR, "setuid %ld: %m", uid); + login_close(llc); + return -1; /* Paranoia again */ + } + + /* + * FreeBSD extension: here we (might) loop and do this twice. + * First, for the class we have been given, and next for + * any user overrides in ~/.login.conf the user's home directory. + */ + if (flags & LOGIN_SETUMASK) + umask(LOGIN_DEFUMASK); /* Set default umask up front */ + + i = 0; + while (i < 2 && lc != NULL) { + + if (flags & LOGIN_SETUMASK) { + rlim_t tmp = login_getcapnum(lc, "umask", RLIM_INFINITY, RLIM_INFINITY); + if (tmp != RLIM_INFINITY) + umask((mode_t)tmp); + } + + if (flags & LOGIN_SETPATH) + setclassenvironment(lc, pwd, 1); + + if (flags & LOGIN_SETENV) + setclassenvironment(lc, pwd, 0); + + if (i++ == 0) /* Play it again, Sam */ + lc = (pwd == NULL) ? NULL : login_getuserclass(pwd); + } + + login_close(lc); /* User's private 'me' class */ + login_close(llc); /* Class we opened */ + + return 0; +} + diff --git a/lib/libutil/login_ok.3 b/lib/libutil/login_ok.3 new file mode 100644 index 0000000..c459d94 --- /dev/null +++ b/lib/libutil/login_ok.3 @@ -0,0 +1,109 @@ +.\" Copyright (c) 1995 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 author +.\" David Nugent. +.\" 5. Modifications may be freely made to this file providing the above +.\" conditions are met. +.\" +.\" $Id$ +.\" +.Dd January 2, 1997 +.Os FreeBSD +.Dt LOGIN_OK 3 +.Sh NAME +.Nm auth_ttyok +.Nm auth_hostok +.Nm auth_timeok +.Nd Functions for checking login class based login restrictions +.Sh SYNOPSIS +.Fd #include <sys/types.h> +.Fd #include <time.h> +.Fd #include <login_cap.h> +.Ft int +.Fn auth_ttyok "login_cap_t *lc" "const char *tty" +.Ft int +.Fn auth_hostok "login_cap_t *lc" "const char *host" "char const *ip" +.Ft int +.Fn auth_timeok "login_cap_t *lc" "time_t t" +.Sh DESCRIPTION +This set of functions checks to see if login is allowed based on login +class capability entries in the login database, +.Xr login.conf 5 . +.Pp +.Fn auth_ttyok +checks to see if the named tty is available to users of a specific +class, and is either in the "ttys.allow" access list, and not in +the "ttys.deny" access list. +An empty "ttys.allowed" list (or if no such capability exists for +the give login class) logins via any tty device are allowed unless +the "ttys.deny" list exists and is non-empty, and the device or its +tty group (see +.Xr ttys 5 ) +is not in the list. +Access to ttys may be allowed or restricted specifically by tty device +name, a device name which includes a wildcard (e.g. ttyD* or cuaD*), +or may name a ttygroup, when group=<name> tags have been assigned in +.Pa /etc/ttys . +Matching of ttys and ttygroups is case sensitive. +Passing a NULL or empty string as the +.Ar tty +parameter causes the function to return a non-zero value. +.Pp +.Fn auth_hostok +checks for any host restrictions for remote logins. +The function checks on both a host name and IP address (given in its +text form, typically n.n.n.n) against the "host.allow" and "host.deny" +login class capabilities. +As with ttys and their groups, wildcards and character classes may be +used in the host allow and deny capability records. +The +.Xr fnmatch 3 +function is used for matching, and the matching on hostnames is case +insensitive. +Note that this function expects that the hostname is fully expanded +(i.e. the local domain name added if necessary) and the IP address +is in its canonical form. +No hostname or address lookups are attempted. +.Pp +It is possible to call this function with either the hostname or +the IP address missing (i.e. NULL) and matching will be performed +only on the basis of the parameter given. +Passing NULL or empty strings in both parameters will result in +a non-zero return value. +.Pp +The +.Fn auth_timeok +function checks to see that a given time value is within the +"times.allow" login class capability and not within the +"times.deny" access lists. +An empty or non-existent "times.allow" list allows access at any +time, except if a given time is falls within a period in the +"times.deny" list. +The format of time period records contained in both "times.allow" +and "times.deny" capability fields is explained in detail in the +.Xr login_times 3 +manual page. +.Sh RETURN VALUES +A non-zero return value from any of these functions indicates that +login access is granted. +A zero return value means either that the item being tested is not +in the "allow" access list, or is within the "deny" access list. +.Sh SEE ALSO +.Xr login.conf 5 , +.Xr login_cap 3 , +.Xr login_class 3 , +.Xr login_times 3 , +.Xr termcap 5 , +.Xr getcap 3 diff --git a/lib/libutil/login_ok.c b/lib/libutil/login_ok.c new file mode 100644 index 0000000..cf778da --- /dev/null +++ b/lib/libutil/login_ok.c @@ -0,0 +1,242 @@ +/*- + * Copyright (c) 1996 by + * 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. + * + * Support allow/deny lists in login class capabilities + * + * $Id$ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <ttyent.h> +#include <fnmatch.h> + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/param.h> +#include <login_cap.h> + + +/* -- support functions -- */ + +/* login_strinlist() + * This function is intentionally public - reused by TAS. + * Returns TRUE (non-zero) if a string matches a pattern + * in a given array of patterns. 'flags' is passed directly + * to fnmatch(3). + */ + +int +login_strinlist(char **list, char const *str, int flags) +{ + int rc = 0; + + if (str != NULL && *str != '\0') + { + int i = 0; + while (rc == 0 && list[i] != NULL) + rc = fnmatch(list[i], str, flags) == 0; + } + return rc; +} + + +/* login_str2inlist() + * Locate either or two strings in a given list + */ + +int +login_str2inlist(char **ttlst, const char *str1, const char *str2, int flags) +{ + int rc = 0; + + if (login_strinlist(ttlst, str1, flags)) + rc = 1; + else if (login_strinlist(ttlst, str2, flags)) + rc = 1; + return rc; +} + + +/* login_timelist() + * This function is intentinoally public - reused by TAS. + * Returns an allocated list of time periods given an array + * of time periods in ascii form. + */ + +login_time_t * +login_timelist(login_cap_t *lc, char const *cap, int *ltno, login_time_t **ltptr) +{ + int j = 0; + struct login_time * lt = NULL; + char **tl = login_getcaplist(lc, cap, NULL); + + if (tl) + { + while (tl[j++] != NULL) + ; + if (*ltno >= j) + lt = *ltptr; + else if ((lt = realloc(*ltptr, j)) != NULL) + { + *ltno = j; + *ltptr = lt; + } + if (lt != NULL) + { + int i = 0; + --j; + while (i < j) + { + lt[i] = parse_lt(tl[i]); + ++i; + } + lt[i].lt_dow = LTM_NONE; + } + } + return lt; +} + + +/* login_ttyok() + * This function is a variation of auth_ttyok(), but it checks two + * arbitrary capability lists not necessarily related to access. + * This hook is provided for the accounted/exclude accounting lists. + */ + +int +login_ttyok(login_cap_t *lc, const char *tty, const char *allowcap, const char *denycap) +{ + int rc = 1; + + if (lc != NULL && tty != NULL && *tty != '\0') + { + struct ttyent * te = getttynam(tty); /* Need group name */ + char * grp = te ? te->ty_group : NULL; + char **ttl = login_getcaplist(lc, allowcap, NULL); + + if (ttl != NULL && !login_str2inlist(ttl, tty, grp, 0)) + rc = 0; /* tty or ttygroup not in allow list */ + else + { + ttl = login_getcaplist(lc, denycap, NULL); + if (ttl != NULL && login_str2inlist(ttl, tty, grp, 0)) + rc = 0; /* tty or ttygroup in deny list */ + } + } + return rc; +} + + +/* auth_ttyok() + * Determine whether or not login on a tty is accessible for + * a login class + */ + +int +auth_ttyok(login_cap_t *lc, const char * tty) +{ + return login_ttyok(lc, tty, "ttys.allow", "ttys.deny"); +} + + +/* login_hostok() + * This function is a variation of auth_hostok(), but it checks two + * arbitrary capability lists not necessarily related to access. + * This hook is provided for the accounted/exclude accounting lists. + */ + +int +login_hostok(login_cap_t *lc, const char *host, const char *ip, const char *allowcap, const char *denycap) +{ + int rc = 1; /* Default is ok */ + + if (lc != NULL && ((host != NULL && *host != '\0') || (ip != NULL && *ip != '\0'))) + { + char **hl = login_getcaplist(lc, allowcap, NULL); + + if (hl != NULL && !login_str2inlist(hl, host, ip, FNM_CASEFOLD)) + rc = 0; /* host or IP not in allow list */ + else + { + hl = login_getcaplist(lc, "host.deny", NULL); + if (hl != NULL && login_str2inlist(hl, host, ip, FNM_CASEFOLD)) + rc = 0; /* host or IP in deny list */ + } + } + return rc; +} + + +/* auth_hostok() + * Determine whether or not login from a host is ok + */ + +int +auth_hostok(login_cap_t *lc, const char *host, const char *ip) +{ + return login_hostok(lc, host, ip, "host.allow", "host.deny"); +} + + +/* auth_timeok() + * Determine whether or not login is ok at a given time + */ + +int +auth_timeok(login_cap_t *lc, time_t t) +{ + int rc = 1; /* Default is ok */ + + if (lc != NULL && t != (time_t)0 && t != (time_t)-1) + { + struct tm * tptr = localtime(&t); + + static int ltimesno = 0; + static struct login_time * ltimes = NULL; + + if (tptr != NULL) + { + struct login_time *lt = login_timelist(lc, "times.allow", <imesno, <imes); + + if (lt != NULL && in_ltms(lt, tptr, NULL) == -1) + rc = 0; /* not in allowed times list */ + else + { + lt = login_timelist(lc, "times.deny", <imesno, <imes); + + if (lt != NULL && in_ltms(lt, tptr, NULL) != -1) + rc = 0; /* in deny times list */ + } + if (ltimes) + { + free(ltimes); + ltimes = NULL; + ltimesno = 0; + } + } + } + return rc; +} + diff --git a/lib/libutil/login_times.3 b/lib/libutil/login_times.3 new file mode 100644 index 0000000..d8e1156 --- /dev/null +++ b/lib/libutil/login_times.3 @@ -0,0 +1,155 @@ +.\" Copyright (c) 1995 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 author +.\" David Nugent. +.\" 5. Modifications may be freely made to this file providing the above +.\" conditions are met. +.\" +.\" $Id$ +.\" +.Dd January 2, 1997 +.Os FreeBSD +.Dt LOGIN_TIMES 3 +.Sh NAME +.Nm parse_lt +.Nm in_ltm +.Nm in_ltms +.Nd Functions for parsing and checking login time periods +.Sh SYNOPSIS +.Fd #include <sys/types.h> +.Fd #include <time.h> +.Fd #include <login_cap.h> +.Ft login_time_t +.Fn parse_lt "const char *str" +.Ft int +.Fn in_ltm "const login_time_t *lt" "struct tm *t" "time_t *ends" +.Ft int +.Fn in_ltms "const login_time_t *lt" "struct tm *t" "time_t *ends" +.Sh DESCRIPTION +This set of functions may be used for parsing and checking login and +session times against a predefined list of allowed login times as +used in +.Xr login.conf 5 . +.Pp +The format of allowed and disallowed session times specified in the +.Ar times.allow +and +.Ar times.deny +capability fields in a login class are comprised of a prefix which +specifies one or more 2- or 3-character day codes, followed by +a start and end time in 24 hour format separated by a hyphen. +Day codes may be concatenated together to select specific days, or +the special mnemonics "Any" and "All" (for any/all days of the week), +"Wk" for any day of the week (excluding Saturdays and Sundays) and +"Wd" for any weekend day may be used. +.Pp +For example, the following time period: +.Dl MoThFrSa1400-2200 +is interpreted as Monday, Thursday through Saturday between the hours +of 2pm and 10pm. +.Dl Wd0600-1800 +means Saturday and Sunday, between the hours of 6am through 6pm, and +.Dl Any0400-1600 +means any day of the week, between 4am and 4pm. +.Pp +Note that all time periods reference system local time. +.Pp +The +.Fn parse_lt +function converts the ascii representation of a time period into +a structure of type +.Ft login_time_t . +This is defined as: +.Bd -literal +typedef struct login_time +{ + u_short lt_start; /* Start time */ + u_short lt_end; /* End time */ + u_char lt_dow; /* Days of week */ +} login_time_t; +.Ed +.Pp +The +.Ar lt_start +and +.Ar lt_end +fields contain the number of minutes past midnight at which the +described period begins and ends. +The +.Ar lt_dow +field is a bit field, containing one bit for each day of the week +and one bit unused. +A series +.Em LTM_* +macros may be used for testing bits individually and in combination. +If no bits are set in this field - ie. it contains the value +.Em LTM_NONE - +then the entire period is assumed invalid. +This is used as a convention to mark the termination of an array +of login_time_t values. +If +.Fn parse_lt +returns a +.Ar login_time_t +with +.Ar lt_dow +equal to +.Em LTM_NONE +then a parsing error was encountered. +.Pp +The remaining functions provide the ability to test a given time_t or +struct tm value against a specific time period or array of time +periods. +.Fn in_ltm +determines whether the given time described by the struct tm +passed as the second parameter falls within the period described +by the first parameter. +A boolean value is returned, indicating whether or not the time +specified falls within the period. +If the time does fall within the time period, and the third +parameter to the function is not NULL, the time at which the +period ends relative to the time passed is returned. +.Pp +The +.Fn in_ltms +function is similar to +.Fn in_ltm +except that the first parameter must be a pointer to an array +of login_time_t objects, which is up to LC_MAXTIMES (64) +elements in length, and terminated by an element with its +.Ar lt_dow +field set to +.Em LTM_NONE. +.Sh RETURN VALUES +.Fn parse_lt +returns a filled in structure of type login_time_t containing the +parsed time period. +If a parsing error occurs, the lt_dow field is set to +.Em LTM_NONE +(i.e. 0). +.Pp +.Fn in_ltm +returns non-zero if the given time falls within the period described +by the login_time_t passed as the first parameter. +.Pp +.Fn in_ltms +returns the index of the first time period found in which the given +time falls, or -1 if none of them apply. +.Sh SEE ALSO +.Xr login.conf 5 , +.Xr login_cap 3 , +.Xr login_class 3 , +.Xr termcap 5 , +.Xr getcap 3 diff --git a/lib/libutil/login_times.c b/lib/libutil/login_times.c new file mode 100644 index 0000000..098200e --- /dev/null +++ b/lib/libutil/login_times.c @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 1996 by + * 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. + * + * Login period parsing and comparison functions. + * + * $Id$ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <ctype.h> + +#include <login_cap.h> + +static struct +{ + const char * dw; + u_char cn; + u_char fl; +} dws[] = +{ + { "su", 2, LTM_SUN }, { "mo", 2, LTM_MON }, { "tu", 2, LTM_TUE }, + { "we", 2, LTM_WED }, { "th", 2, LTM_THU }, { "fr", 2, LTM_FRI }, + { "sa", 2, LTM_SAT }, { "any",3, LTM_ANY }, { "all",3, LTM_ANY }, + { "wk", 2, LTM_WK }, { "wd", 2, LTM_WD }, { NULL, 0, 0 } +}; + +static char * +parse_time(char * ptr, u_short * t) +{ + u_short val; + + for (val = 0; *ptr && isdigit(*ptr); ptr++) + val = (u_short)(val * 10 + (*ptr - '0')); + + *t = (u_short)((val / 100) * 60 + (val % 100)); + return ptr; +} + +login_time_t +parse_lt(const char * str) +{ + login_time_t t; + + memset(&t, 0, sizeof t); + t.lt_dow = LTM_NONE; + if (str && *str && strcmp(str, "Never") != 0 && strcmp(str, "None") != 0) + { + int i; + login_time_t m = t; + char * p; + char buf[64]; + + /* Make local copy and force lowercase to simplify parsing + */ + p = strncpy(buf, str, sizeof buf); + buf[sizeof buf - 1] = '\0'; + for (i = 0; buf[i]; i++) + buf[i] = (char)tolower(buf[i]); + + while (isalpha(*p)) + { + for (i = 0; dws[i].dw && strncmp(p, dws[i].dw, dws[i].cn) != 0 ; i++) + ; + if (dws[i].dw == NULL) + break; + m.lt_dow |= dws[i].fl; + p += dws[i].cn; + } + + if (m.lt_dow == LTM_NONE) /* No (valid) prefix, assume any */ + m.lt_dow |= LTM_ANY; + + if (isdigit(*p)) + p = parse_time(p, &m.lt_start); + else + m.lt_start = 0; + if (*p == '-') + p = parse_time(++p, &m.lt_end); + else + m.lt_end = 1440; + + t = m; + } + return t; +} + + +int +in_ltm(const login_time_t * ltm, struct tm * tt, time_t * ends) +{ + int rc = 0; + + if (tt != NULL) + { + /* First, examine the day of the week + */ + if ((u_char)(0x01 << tt->tm_wday) & ltm->lt_dow) + { + /* Convert `current' time to minute of the day + */ + u_short now = (u_short)((tt->tm_hour * 60) + tt->tm_min); + if (tt->tm_sec > 30) + ++now; + if (now >= ltm->lt_start && now < ltm->lt_end) + { + rc = 2; + if (ends != NULL) + { + /* If requested, return ending time for this period + */ + tt->tm_hour = (int)(ltm->lt_end / 60); + tt->tm_min = (int)(ltm->lt_end % 60); + *ends = mktime(tt); + } + } + } + } + return rc; +} + +int +in_lt(const login_time_t * ltm, time_t * t) +{ + return in_ltm(ltm, localtime(t), t); +} + +int +in_ltms(const login_time_t * ltm, struct tm * tm, time_t * t) +{ + int i = 0; + + while (i < LC_MAXTIMES && ltm[i].lt_dow != LTM_NONE) + { + if (in_ltm(ltm + i, tm, t)) + return i; + i++; + } + return -1; +} + +int +in_lts(const login_time_t * ltm, time_t * t) +{ + return in_ltms(ltm, localtime(t), t); +} + |