login_access.c revision 22230
1139804Simp /* 21541Srgrimes * This module implements a simple but effective form of login access 31541Srgrimes * control based on login names and on host (or domain) names, internet 41541Srgrimes * addresses (or network numbers), or on terminal line names in case of 51541Srgrimes * non-networked logins. Diagnostics are reported through syslog(3). 61541Srgrimes * 71541Srgrimes * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. 81541Srgrimes */ 91541Srgrimes 101541Srgrimes#ifdef LOGIN_ACCESS 111541Srgrimes#ifndef lint 121541Srgrimesstatic char sccsid[] = "%Z% %M% %I% %E% %U%"; 131541Srgrimes#endif 141541Srgrimes 151541Srgrimes#include <stdio.h> 161541Srgrimes#include <syslog.h> 171541Srgrimes#include <ctype.h> 181541Srgrimes#include <sys/types.h> 191541Srgrimes#include <grp.h> 201541Srgrimes#include <errno.h> 211541Srgrimes#include <string.h> 221541Srgrimes#include <unistd.h> 231541Srgrimes#include <stdlib.h> 241541Srgrimes 251541Srgrimes#include "pathnames.h" 261541Srgrimes 271541Srgrimes /* Delimiters for fields and for lists of users, ttys or hosts. */ 281541Srgrimes 291541Srgrimesstatic char fs[] = ":"; /* field separator */ 301541Srgrimesstatic char sep[] = ", \t"; /* list-element separator */ 311541Srgrimes 321541Srgrimes /* Constants to be used in assignments only, not in comparisons... */ 331541Srgrimes 3414526Shsu#define YES 1 351541Srgrimes#define NO 0 361541Srgrimes 37116182Sobrienstatic int list_match(); 38116182Sobrienstatic int user_match(); 39116182Sobrienstatic int from_match(); 4014328Speterstatic int string_match(); 41217688Spluknet 4280418Speter/* login_access - match username/group and host/tty with access control file */ 4313226Swollman 44202143Sbrooksint 451541Srgrimeslogin_access(user, from) 4680418Speterchar *user; 4780418Speterchar *from; 48172696Salfred{ 49217688Spluknet FILE *fp; 501541Srgrimes char line[BUFSIZ]; 51137393Sdes char *perm; /* becomes permission field */ 5284783Sps char *users; /* becomes list of login names */ 531541Srgrimes char *froms; /* becomes list of terminals or hosts */ 541541Srgrimes int match = NO; 551541Srgrimes int end; 561541Srgrimes int lineno = 0; /* for diagnostics */ 571541Srgrimes 58209492Snwhitehorn /* 59209492Snwhitehorn * Process the table one line at a time and stop at the first match. 60209492Snwhitehorn * Blank lines and lines that begin with a '#' character are ignored. 61137307Sphk * Non-comment lines are broken at the ':' character. All fields are 62138214Sbms * mandatory. The first field should be a "+" or "-" character. A 63184323Ssobomax * non-existing table means no access control. 64195430Ssilby */ 65184323Ssobomax 66184323Ssobomax if ((fp = fopen(_PATH_LOGACCESS, "r")) != NULL) { 67184323Ssobomax while (!match && fgets(line, sizeof(line), fp)) { 68184323Ssobomax lineno++; 69184323Ssobomax if (line[end = strlen(line) - 1] != '\n') { 701541Srgrimes syslog(LOG_ERR, "%s: line %d: missing newline or line too long", 7180418Speter _PATH_LOGACCESS, lineno); 7280418Speter continue; 7380418Speter } 7480418Speter if (line[0] == '#') 7545515Sdes continue; /* comment line */ 7680418Speter while (end > 0 && isspace(line[end - 1])) 7745515Sdes end--; 788747Sdg line[end] = 0; /* strip trailing whitespace */ 79186286Sivoras if (line[0] == 0) /* skip blank lines */ 80186286Sivoras continue; 8180418Speter if (!(perm = strtok(line, fs)) 8280418Speter || !(users = strtok((char *) 0, fs)) 8380418Speter || !(froms = strtok((char *) 0, fs)) 8480418Speter || strtok((char *) 0, fs)) { 8580418Speter syslog(LOG_ERR, "%s: line %d: bad field count", _PATH_LOGACCESS, 8680418Speter lineno); 8780418Speter continue; 88217688Spluknet } 8980418Speter if (perm[0] != '+' && perm[0] != '-') { 9080418Speter syslog(LOG_ERR, "%s: line %d: bad first field", _PATH_LOGACCESS, 91202143Sbrooks lineno); 9280418Speter continue; 93189595Sjhb } 94189595Sjhb match = (list_match(froms, from, from_match) 95189649Sjhb && list_match(users, user, user_match)); 96186286Sivoras } 97137393Sdes (void) fclose(fp); 98137393Sdes } else if (errno != ENOENT) { 99137393Sdes syslog(LOG_ERR, "cannot open %s: %m", _PATH_LOGACCESS); 100137393Sdes } 101137393Sdes return (match == 0 || (line[0] == '+')); 102137393Sdes} 1031541Srgrimes 104190331Sjhb/* list_match - match an item against a list of tokens with exceptions */ 105190331Sjhb 106189744Sjhbstatic int list_match(list, item, match_fn) 107189744Sjhbchar *list; 108189744Sjhbchar *item; 109190331Sjhbint (*match_fn) (); 110189744Sjhb{ 111189744Sjhb char *tok; 112217688Spluknet int match = NO; 113217688Spluknet 114189745Sjhb /* 115190331Sjhb * Process tokens one at a time. We have exhausted all possible matches 116189745Sjhb * when we reach an "EXCEPT" token or the end of the list. If we do find 117190331Sjhb * a match, look for an "EXCEPT" list and recurse to determine whether 118178872Spjd * the match is affected by any exceptions. 119190331Sjhb */ 120178872Spjd 121190331Sjhb for (tok = strtok(list, sep); tok != 0; tok = strtok((char *) 0, sep)) { 122178872Spjd if (strcasecmp(tok, "EXCEPT") == 0) /* EXCEPT: give up */ 123190331Sjhb break; 124178872Spjd if ((match = (*match_fn)(tok, item)) != NULL) /* YES */ 125190331Sjhb break; 126178872Spjd } 127190331Sjhb /* Process exceptions to matches. */ 128178872Spjd 129190331Sjhb if (match != NO) { 130186286Sivoras while ((tok = strtok((char *) 0, sep)) && strcasecmp(tok, "EXCEPT")) 131186286Sivoras /* VOID */ ; 132204611Sivoras if (tok == 0 || list_match((char *) 0, item, match_fn) == NO) 133172696Salfred return (match); 1341541Srgrimes } 1351541Srgrimes return (NO); 1361541Srgrimes} 1371541Srgrimes 1381541Srgrimes/* netgroup_match - match group against machine or user */ 1399759Sbde 14067046Sjasonestatic int netgroup_match(group, machine, user) 141204420Salcgid_t group; 142204420Salcchar *machine; 143204420Salcchar *user; 144204420Salc{ 145186522Sbz#ifdef NIS 146186522Sbz static char *mydomain = 0; 147186522Sbz 148186522Sbz if (mydomain == 0) 149186522Sbz yp_get_default_domain(&mydomain); 150186522Sbz return (innetgr(group, machine, user, mydomain)); 151186522Sbz#else 152186522Sbz syslog(LOG_ERR, "NIS netgroup support not configured"); 153185772Sjkim return 0; 154185772Sjkim#endif 155185772Sjkim} 156185772Sjkim 157210935Scsjp/* user_match - match a username against one token */ 158185772Sjkim 159185772Sjkimstatic int user_match(tok, string) 160185772Sjkimchar *tok; 161184326Ssobomaxchar *string; 162184323Ssobomax{ 163184323Ssobomax struct group *group; 164184323Ssobomax int i; 165184323Ssobomax 166184323Ssobomax /* 167184323Ssobomax * If a token has the magic value "ALL" the match always succeeds. 168184323Ssobomax * Otherwise, return YES if the token fully matches the username, or if 169186286Sivoras * the token is a group that contains the username. 170186286Sivoras */ 171186286Sivoras 172186286Sivoras if (tok[0] == '@') { /* netgroup */ 173186252Sivoras return (netgroup_match(tok + 1, (char *) 0, string)); 174184323Ssobomax } else if (string_match(tok, string)) { /* ALL or exact match */ 175184323Ssobomax return (YES); 176184323Ssobomax } else if ((group = getgrnam(tok)) != NULL) {/* try group membership */ 177184323Ssobomax for (i = 0; group->gr_mem[i]; i++) 178184323Ssobomax if (strcasecmp(string, group->gr_mem[i]) == 0) 179185772Sjkim return (YES); 180185772Sjkim } 181185772Sjkim return (NO); 182185772Sjkim} 183185772Sjkim 184186252Sivoras/* from_match - match a host or tty against a list of tokens */ 185185772Sjkim 186185772Sjkimstatic int from_match(tok, string) 187185772Sjkimchar *tok; 188184323Ssobomaxchar *string; 189184323Ssobomax{ 190185772Sjkim int tok_len; 191185772Sjkim int str_len; 192185772Sjkim 193186252Sivoras /* 194185772Sjkim * If a token has the magic value "ALL" the match always succeeds. Return 195185772Sjkim * YES if the token fully matches the string. If the token is a domain 196184323Ssobomax * name, return YES if it matches the last fields of the string. If the 197186252Sivoras * token has the magic value "LOCAL", return YES if the string does not 198184323Ssobomax * contain a "." character. If the token is a network number, return YES 199186522Sbz * if it matches the head of the string. 200184323Ssobomax */ 20167046Sjasone 20287546Sdillon if (tok[0] == '@') { /* netgroup */ 20380418Speter return (netgroup_match(tok + 1, string, (char *) 0)); 20480418Speter } else if (string_match(tok, string)) { /* ALL or exact match */ 20587546Sdillon return (YES); 20680418Speter } else if (tok[0] == '.') { /* domain: match last fields */ 207186252Sivoras if ((str_len = strlen(string)) > (tok_len = strlen(tok)) 208186252Sivoras && strcasecmp(tok, string + str_len - tok_len) == 0) 209186252Sivoras return (YES); 210186252Sivoras } else if (strcasecmp(tok, "LOCAL") == 0) { /* local: no dots */ 211186252Sivoras if (strchr(string, '.') == 0) 212184323Ssobomax return (YES); 21380418Speter } else if (tok[(tok_len = strlen(tok)) - 1] == '.' /* network */ 214185772Sjkim && strncmp(tok, string, tok_len) == 0) { 215186252Sivoras return (YES); 21680418Speter } 21780418Speter return (NO); 21881986Sdillon} 21981933Sdillon 22081986Sdillon/* string_match - match a string against one token */ 221189595Sjhb 22281986Sdillonstatic int string_match(tok, string) 22381933Sdillonchar *tok; 22481986Sdillonchar *string; 225189595Sjhb{ 226217688Spluknet 227217688Spluknet /* 22884783Sps * If the token has the magic value "ALL" the match always succeeds. 22984783Sps * Otherwise, return YES if the token fully matches the string. 230137393Sdes */ 23184783Sps 232137393Sdes if (strcasecmp(tok, "ALL") == 0) { /* all: always matches */ 23384783Sps return (YES); 234137393Sdes } else if (strcasecmp(tok, string) == 0) { /* try exact match */ 23584783Sps return (YES); 236137393Sdes } 23784783Sps return (NO); 238137393Sdes} 23984783Sps#endif /* LOGIN_ACCES */ 240137393Sdes