login_auth.c revision 21332
1/*- 2 * Copyright (c) 1996 by 3 * Sean Eric Fagan <sef@kithrup.com> 4 * David Nugent <davidn@blaze.net.au> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, is permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice immediately at the beginning of the file, without modification, 12 * this list of conditions, and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. This work was done expressly for inclusion into FreeBSD. Other use 17 * is permitted provided this notation is included. 18 * 4. Absolutely no warranty of function or purpose is made by the authors. 19 * 5. Modifications may be freely made to this file providing the above 20 * conditions are met. 21 * 22 * Low-level routines relating to the user capabilities database 23 * 24 * $Id: login_auth.c,v 1.1 1997/01/04 16:49:59 davidn Exp $ 25 */ 26 27#include <sys/types.h> 28#include <sys/time.h> 29#include <sys/resource.h> 30#include <sys/stat.h> 31#include <errno.h> 32#include <fcntl.h> 33#include <limits.h> 34#include <stdio.h> 35#include <pwd.h> 36#include <stdlib.h> 37#include <string.h> 38#include <syslog.h> 39#include <unistd.h> 40#include <login_cap.h> 41#include <stdarg.h> 42#include <paths.h> 43#include <sys/wait.h> 44 45extern char *fgetline(FILE *, int*); 46 47#ifdef RLIM_LONG 48# define STRTOV strtol 49#else 50# define STRTOV strtoq 51#endif 52 53#define AUTHMAXLINES 1024 54#define AUTHMAXARGS 16 55 56struct auth_info { 57 int reject; 58 int auths; 59 int env_count; 60 char **env; 61 int file_count; 62 char **files; 63}; 64 65static struct auth_info auth_info; 66 67/* 68 * free_auth_info() 69 * Go through the auth_info structure, and free() anything of interest. 70 * This includes the string arrays, and any individual element. 71 * All part of being environmentally conscious ;). 72 */ 73 74static void 75free_auth_info(void) 76{ 77 int i; 78 char *ptr; 79 80 auth_info.reject = 0; 81 auth_info.auths = 0; 82 if (auth_info.env) { 83 for (i = 0; i < auth_info.env_count; i++) { 84 if (auth_info.env[i]) 85 free(auth_info.env[i]); 86 } 87 free(auth_info.env); 88 auth_info.env = NULL; 89 } 90 if (auth_info.files) { 91 for (i = 0; i < auth_info.file_count; i++) { 92 if (auth_info.files[i]) 93 free(auth_info.files[i]); 94 } 95 free(auth_info.files); 96 auth_info.files = NULL; 97 } 98} 99 100 101/* 102 * collect_info() 103 * Read from <fd>, a list of authorization commands. 104 * These commands are: 105 * reject 106 * authorize [root|secure] 107 * setenv <name>[ <value>] 108 * remove <file> 109 * A single reject means the entire thing is bad; 110 * multiple authorize statements can be present (it would be 111 * silly, but that's what the spec says). 112 * The commands are collected, and are accted upon by: 113 * auth_scan() -- check for authorization or rejection 114 * auth_rmfiles() -- remove the specified files 115 * auth_env() -- set the specified environment variables 116 * We only get up to AUTHMAXLINES lines of input from the program. 117 */ 118#define STRSIZEOF(x) (sizeof(x)-1) 119static void 120collect_info(int fd) 121{ 122 char *line; 123 FILE *fp; 124 char *ptr; 125 int len; 126 int line_count = 0; 127 128 fp = fdopen(fd, "r"); 129 130 while ((line = fgetline(fp, &len)) != NULL) { 131 if (++line_count > AUTHMAXLINES) 132 break; 133 if (strncasecmp(line, BI_REJECT, STRSIZEOF(BI_REJECT)) == 0) { 134 auth_info.reject = 1; 135 } else if (strncasecmp(line, BI_AUTH, STRSIZEOF(BI_AUTH)) == 0) { 136 ptr = line + STRSIZEOF(BI_AUTH); 137 ptr += strspn(ptr, " \t"); 138 if (!*ptr) 139 auth_info.auths |= AUTH_OKAY; 140 else if (strncasecmp(ptr, BI_ROOTOKAY, STRSIZEOF(BI_ROOTOKAY)) == 0) 141 auth_info.auths |= AUTH_ROOTOKAY; 142 else if (strncasecmp(ptr, BI_SECURE, STRSIZEOF(BI_SECURE)) == 0) 143 auth_info.auths |= AUTH_SECURE; 144 } else if (strncasecmp(line, BI_SETENV, STRSIZEOF(BI_SETENV)) == 0) { 145 ptr = line + STRSIZEOF(BI_SETENV); 146 ptr += strspn(ptr, " \t"); 147 if (*ptr) { 148 char **tmp = realloc(auth_info.env, sizeof(char*) * (auth_info.env_count + 1)); 149 if (tmp != NULL) { 150 auth_info.env = tmp; 151 if ((auth_info.env[auth_info.env_count] = strdup(ptr)) != NULL) 152 auth_info.env_count++; 153 } 154 } 155 } else if (strncasecmp(line, BI_REMOVE, STRSIZEOF(BI_REMOVE)) == 0) { 156 ptr = line + STRSIZEOF(BI_REMOVE); 157 ptr += strspn(ptr, " \t"); 158 if (*ptr) { 159 char **tmp = realloc(auth_info.files, sizeof(char*) * (auth_info.file_count + 1)); 160 if (tmp != NULL) { 161 auth_info.files = tmp; 162 if ((auth_info.files[auth_info.file_count] = strdup(ptr)) != NULL) 163 auth_info.file_count++; 164 } 165 } 166 } 167 } 168 fclose(fp); 169} 170 171 172/* 173 * authenticate() 174 * Starts an auth_script() for the given <user>, with a class <class>, 175 * style <style>, and service <service>. <style> is necessary, 176 * as are <user> and <class>, but <service> is optional -- it defaults 177 * to "login". 178 * Since auth_script() expects an execl'able program name, authenticate() 179 * also concatenates <style> to _PATH_AUTHPROG. 180 * Lastly, calls auth_scan(AUTH_NONE) to see if there are any "reject" statements, 181 * or lack of "auth" statements. 182 * Returns -1 on error, 0 on rejection, and >0 on success. 183 * (See AUTH_* for the return values.) 184 * 185 */ 186int 187authenticate(const char * name, const char * class, const char * style, const char *service) 188{ 189 int retval; 190 191 if (style == NULL || *style == '\0') 192 retval = -1; 193 else { 194 char buf[sizeof(_PATH_AUTHPROG) + 64]; 195 196 if (service == NULL || *service == '\0') 197 service = LOGIN_DEFSERVICE; 198 199 free_auth_info(); 200 201 if (snprintf(buf, sizeof buf, _PATH_AUTHPROG "%s", style) >= sizeof buf) 202 retval = -1; 203 else { 204 retval = auth_script(buf, style, "-s", service, name, class, NULL); 205 if (retval >= 0) 206 retval = auth_scan(AUTH_NONE); 207 } 208 } 209 return retval; 210} 211 212 213/* 214 * auth_script() 215 * Runs an authentication program with specified arguments. 216 * It sets up file descriptor 3 for the program to write to; 217 * it stashes the output somewhere. The output of the program 218 * consists of statements: 219 * reject 220 * authorize [root|secure] 221 * setenv <name> [<value>] 222 * remove <file> 223 * 224 * Terribly exciting, isn't it? There is no limit specified in 225 * BSDi's API for how much output can be present, but we should 226 * keep it fairly small, I think. 227 * No more than AUTHMAXLINES lines. 228 */ 229 230int 231auth_script(const char * path, ...) 232{ 233 va_list ap; 234 int pid, status; 235 int argc = 0; 236 int p[2]; /* pipes */ 237 char *argv[AUTHMAXARGS+1]; 238 239 va_start(ap, path); 240 while (argc < AUTHMAXARGS && (argv[argc++] = va_arg(ap, char*)) != NULL) 241 ; 242 argv[argc] = NULL; 243 va_end(ap); 244 245 fflush(NULL); 246 247 if (pipe(p) >= 0) { 248 if ((pid = fork()) == -1) { 249 close(p[0]); 250 close(p[1]); 251 } else if (pid == 0) { /* Child */ 252 close(p[0]); 253 dup2(p[1], 3); 254 if (setenv("PATH", _PATH_DEFPATH, 1)==0 && setenv("SHELL", _PATH_BSHELL, 1)==0) 255 execv(path, argv); 256 _exit(1); 257 } else { 258 close(p[1]); 259 collect_info(p[0]); 260 if (waitpid(pid, &status, 0) != -1 && WIFEXITED(status) && !WEXITSTATUS(status)) 261 return 0; 262 } 263 } 264 return -1; 265} 266 267 268/* 269 * auth_env() 270 * Processes the stored "setenv" lines from the stored authentication 271 * output. 272 */ 273 274int 275auth_env(void) 276{ 277 int i; 278 279 for (i = 0; i < auth_info.env_count; i++) { 280 char *nam = auth_info.env[i]; 281 char *ptr = nam + strcspn(nam, " \t="); 282 if (*ptr) { 283 *ptr++ = '\0'; 284 ptr += strspn(ptr, " \t"); 285 } 286 setenv(nam, ptr, 1); 287 } 288} 289 290 291/* 292 * auth_scan() 293 * Goes through the output of the auth_script/authenticate, and 294 * checks for a failure or authentication. 295 * <ok> is a default authentication value -- if there are no 296 * rejection or authentication statements, then it is returned 297 * unmodified. 298 * AUTH_NONE is returned if there were any reject statements 299 * from the authentication program (invoked by auth_script()), and 300 * AUTH, AUTH_ROOTOKAY, and/or AUTH_SECURE are returned if the 301 * appropriate directives were found. Note that AUTH* are 302 * *bitmasks*! 303 */ 304 305int 306auth_scan(int ok) 307{ 308 if (auth_info.reject) 309 return 0; 310 return ok | auth_info.auths; 311} 312 313 314/* 315 * auth_rmfiles() 316 * Removes any files that the authentication program said needed to be 317 * removed, said files having come from a previous execution of 318 * auth_script(). 319 */ 320 321int 322auth_rmfiles(void) 323{ 324 int i = auth_info.file_count; 325 while (i-- > 0) { 326 unlink(auth_info.files[i]); 327 free(auth_info.files[i]); 328 auth_info.files[i] = NULL; 329 } 330} 331 332 333/* 334 * auth_checknologin() 335 * Checks for the existance of a nologin file in the login_cap 336 * capability <lc>. If there isn't one specified, then it checks 337 * to see if this class should just ignore nologin files. Lastly, 338 * it tries to print out the default nologin file, and, if such 339 * exists, it exits. 340 */ 341 342void 343auth_checknologin(login_cap_t *lc) 344{ 345 char *file; 346 347 /* Do we ignore a nologin file? */ 348 if (login_getcapbool(lc, "ignorenologin", 0)) 349 return; 350 351 /* Note that <file> will be "" if there is no nologin capability */ 352 if ((file = login_getcapstr(lc, "nologin", "", NULL)) == NULL) 353 exit(1); 354 355 /* 356 * *file is true IFF there was a "nologin" capability 357 * Note that auth_cat() returns 1 only if the specified 358 * file exists, and is readable. E.g., /.nologin exists. 359 */ 360 if ((*file && auth_cat(file)) || auth_cat(_PATH_NOLOGIN)) 361 exit(1); 362} 363 364 365/* 366 * auth_cat() 367 * Checks for the readability of <file>; if it can be opened for 368 * reading, it prints it out to stdout, and then exits. Otherwise, 369 * it returns 0 (meaning no nologin file). 370 */ 371int 372auth_cat(const char *file) 373{ 374 int fd, count; 375 char buf[BUFSIZ]; 376 377 if ((fd = open(file, O_RDONLY)) < 0) 378 return 0; 379 while ((count = read(fd, buf, sizeof(buf))) > 0) 380 write(fileno(stdout), buf, count); 381 close(fd); 382 return 1; 383} 384