login_auth.c revision 22086
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 * $FreeBSD: head/lib/libutil/login_auth.c 22086 1997-01-29 06:11:31Z davidn $ 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 79 auth_info.reject = 0; 80 auth_info.auths = 0; 81 if (auth_info.env) { 82 for (i = 0; i < auth_info.env_count; i++) { 83 if (auth_info.env[i]) 84 free(auth_info.env[i]); 85 } 86 free(auth_info.env); 87 auth_info.env = NULL; 88 } 89 if (auth_info.files) { 90 for (i = 0; i < auth_info.file_count; i++) { 91 if (auth_info.files[i]) 92 free(auth_info.files[i]); 93 } 94 free(auth_info.files); 95 auth_info.files = NULL; 96 } 97} 98 99 100/* 101 * collect_info() 102 * Read from <fd>, a list of authorization commands. 103 * These commands are: 104 * reject 105 * authorize [root|secure] 106 * setenv <name>[ <value>] 107 * remove <file> 108 * A single reject means the entire thing is bad; 109 * multiple authorize statements can be present (it would be 110 * silly, but that's what the spec says). 111 * The commands are collected, and are accted upon by: 112 * auth_scan() -- check for authorization or rejection 113 * auth_rmfiles() -- remove the specified files 114 * auth_env() -- set the specified environment variables 115 * We only get up to AUTHMAXLINES lines of input from the program. 116 */ 117#define STRSIZEOF(x) (sizeof(x)-1) 118static void 119collect_info(int fd) 120{ 121 char *line; 122 FILE *fp; 123 char *ptr; 124 int len; 125 int line_count = 0; 126 127 fp = fdopen(fd, "r"); 128 129 while ((line = fgetline(fp, &len)) != NULL) { 130 if (++line_count > AUTHMAXLINES) 131 break; 132 if (strncasecmp(line, BI_REJECT, STRSIZEOF(BI_REJECT)) == 0) { 133 auth_info.reject = 1; 134 } else if (strncasecmp(line, BI_AUTH, STRSIZEOF(BI_AUTH)) == 0) { 135 ptr = line + STRSIZEOF(BI_AUTH); 136 ptr += strspn(ptr, " \t"); 137 if (!*ptr) 138 auth_info.auths |= AUTH_OKAY; 139 else if (strncasecmp(ptr, BI_ROOTOKAY, STRSIZEOF(BI_ROOTOKAY)) == 0) 140 auth_info.auths |= AUTH_ROOTOKAY; 141 else if (strncasecmp(ptr, BI_SECURE, STRSIZEOF(BI_SECURE)) == 0) 142 auth_info.auths |= AUTH_SECURE; 143 } else if (strncasecmp(line, BI_SETENV, STRSIZEOF(BI_SETENV)) == 0) { 144 ptr = line + STRSIZEOF(BI_SETENV); 145 ptr += strspn(ptr, " \t"); 146 if (*ptr) { 147 char **tmp = realloc(auth_info.env, sizeof(char*) * (auth_info.env_count + 1)); 148 if (tmp != NULL) { 149 auth_info.env = tmp; 150 if ((auth_info.env[auth_info.env_count] = strdup(ptr)) != NULL) 151 auth_info.env_count++; 152 } 153 } 154 } else if (strncasecmp(line, BI_REMOVE, STRSIZEOF(BI_REMOVE)) == 0) { 155 ptr = line + STRSIZEOF(BI_REMOVE); 156 ptr += strspn(ptr, " \t"); 157 if (*ptr) { 158 char **tmp = realloc(auth_info.files, sizeof(char*) * (auth_info.file_count + 1)); 159 if (tmp != NULL) { 160 auth_info.files = tmp; 161 if ((auth_info.files[auth_info.file_count] = strdup(ptr)) != NULL) 162 auth_info.file_count++; 163 } 164 } 165 } 166 } 167 fclose(fp); 168} 169 170 171/* 172 * authenticate() 173 * Starts an auth_script() for the given <user>, with a class <class>, 174 * style <style>, and service <service>. <style> is necessary, 175 * as are <user> and <class>, but <service> is optional -- it defaults 176 * to "login". 177 * Since auth_script() expects an execl'able program name, authenticate() 178 * also concatenates <style> to _PATH_AUTHPROG. 179 * Lastly, calls auth_scan(AUTH_NONE) to see if there are any "reject" statements, 180 * or lack of "auth" statements. 181 * Returns -1 on error, 0 on rejection, and >0 on success. 182 * (See AUTH_* for the return values.) 183 * 184 */ 185int 186authenticate(const char * name, const char * class, const char * style, const char *service) 187{ 188 int retval; 189 190 if (style == NULL || *style == '\0') 191 retval = -1; 192 else { 193 char buf[sizeof(_PATH_AUTHPROG) + 64]; 194 195 if (service == NULL || *service == '\0') 196 service = LOGIN_DEFSERVICE; 197 198 free_auth_info(); 199 200 if (snprintf(buf, sizeof buf, _PATH_AUTHPROG "%s", style) >= sizeof buf) 201 retval = -1; 202 else { 203 retval = auth_script(buf, style, "-s", service, name, class, NULL); 204 if (retval >= 0) 205 retval = auth_scan(AUTH_NONE); 206 } 207 } 208 return retval; 209} 210 211 212/* 213 * auth_script() 214 * Runs an authentication program with specified arguments. 215 * It sets up file descriptor 3 for the program to write to; 216 * it stashes the output somewhere. The output of the program 217 * consists of statements: 218 * reject 219 * authorize [root|secure] 220 * setenv <name> [<value>] 221 * remove <file> 222 * 223 * Terribly exciting, isn't it? There is no limit specified in 224 * BSDi's API for how much output can be present, but we should 225 * keep it fairly small, I think. 226 * No more than AUTHMAXLINES lines. 227 */ 228 229int 230auth_script(const char * path, ...) 231{ 232 va_list ap; 233 int pid, status; 234 int argc = 0; 235 int p[2]; /* pipes */ 236 char *argv[AUTHMAXARGS+1]; 237 238 va_start(ap, path); 239 while (argc < AUTHMAXARGS && (argv[argc++] = va_arg(ap, char*)) != NULL) 240 ; 241 argv[argc] = NULL; 242 va_end(ap); 243 244 fflush(NULL); 245 246 if (pipe(p) >= 0) { 247 if ((pid = fork()) == -1) { 248 close(p[0]); 249 close(p[1]); 250 } else if (pid == 0) { /* Child */ 251 close(p[0]); 252 dup2(p[1], 3); 253 if (setenv("PATH", _PATH_DEFPATH, 1)==0 && setenv("SHELL", _PATH_BSHELL, 1)==0) 254 execv(path, argv); 255 _exit(1); 256 } else { 257 close(p[1]); 258 collect_info(p[0]); 259 if (waitpid(pid, &status, 0) != -1 && WIFEXITED(status) && !WEXITSTATUS(status)) 260 return 0; 261 } 262 } 263 return -1; 264} 265 266 267/* 268 * auth_env() 269 * Processes the stored "setenv" lines from the stored authentication 270 * output. 271 */ 272 273int 274auth_env(void) 275{ 276 int i; 277 278 for (i = 0; i < auth_info.env_count; i++) { 279 char *nam = auth_info.env[i]; 280 char *ptr = nam + strcspn(nam, " \t="); 281 if (*ptr) { 282 *ptr++ = '\0'; 283 ptr += strspn(ptr, " \t"); 284 } 285 setenv(nam, ptr, 1); 286 } 287 return 0; 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 return 0; 331} 332 333 334/* 335 * auth_checknologin() 336 * Checks for the existance of a nologin file in the login_cap 337 * capability <lc>. If there isn't one specified, then it checks 338 * to see if this class should just ignore nologin files. Lastly, 339 * it tries to print out the default nologin file, and, if such 340 * exists, it exits. 341 */ 342 343void 344auth_checknologin(login_cap_t *lc) 345{ 346 char *file; 347 348 /* Do we ignore a nologin file? */ 349 if (login_getcapbool(lc, "ignorenologin", 0)) 350 return; 351 352 /* Note that <file> will be "" if there is no nologin capability */ 353 if ((file = login_getcapstr(lc, "nologin", "", NULL)) == NULL) 354 exit(1); 355 356 /* 357 * *file is true IFF there was a "nologin" capability 358 * Note that auth_cat() returns 1 only if the specified 359 * file exists, and is readable. E.g., /.nologin exists. 360 */ 361 if ((*file && auth_cat(file)) || auth_cat(_PATH_NOLOGIN)) 362 exit(1); 363} 364 365 366/* 367 * auth_cat() 368 * Checks for the readability of <file>; if it can be opened for 369 * reading, it prints it out to stdout, and then exits. Otherwise, 370 * it returns 0 (meaning no nologin file). 371 */ 372int 373auth_cat(const char *file) 374{ 375 int fd, count; 376 char buf[BUFSIZ]; 377 378 if ((fd = open(file, O_RDONLY)) < 0) 379 return 0; 380 while ((count = read(fd, buf, sizeof(buf))) > 0) 381 write(fileno(stdout), buf, count); 382 close(fd); 383 return 1; 384} 385