login_auth.c revision 22541
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 22541 1997-02-10 16:32:03Z 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 45#ifdef RLIM_LONG 46# define STRTOV strtol 47#else 48# define STRTOV strtoq 49#endif 50 51#define AUTHMAXLINES 1024 52#define AUTHMAXARGS 16 53 54struct auth_info { 55 int reject; 56 int auths; 57 int env_count; 58 char **env; 59 int file_count; 60 char **files; 61}; 62 63static struct auth_info auth_info; 64 65/* 66 * free_auth_info() 67 * Go through the auth_info structure, and free() anything of interest. 68 * This includes the string arrays, and any individual element. 69 * All part of being environmentally conscious ;). 70 */ 71 72static void 73free_auth_info(void) 74{ 75 int i; 76 77 auth_info.reject = 0; 78 auth_info.auths = 0; 79 if (auth_info.env) { 80 for (i = 0; i < auth_info.env_count; i++) { 81 if (auth_info.env[i]) 82 free(auth_info.env[i]); 83 } 84 free(auth_info.env); 85 auth_info.env = NULL; 86 } 87 if (auth_info.files) { 88 for (i = 0; i < auth_info.file_count; i++) { 89 if (auth_info.files[i]) 90 free(auth_info.files[i]); 91 } 92 free(auth_info.files); 93 auth_info.files = NULL; 94 } 95} 96 97 98/* 99 * collect_info() 100 * Read from <fd>, a list of authorization commands. 101 * These commands are: 102 * reject 103 * authorize [root|secure] 104 * setenv <name>[ <value>] 105 * remove <file> 106 * A single reject means the entire thing is bad; 107 * multiple authorize statements can be present (it would be 108 * silly, but that's what the spec says). 109 * The commands are collected, and are accted upon by: 110 * auth_scan() -- check for authorization or rejection 111 * auth_rmfiles() -- remove the specified files 112 * auth_env() -- set the specified environment variables 113 * We only get up to AUTHMAXLINES lines of input from the program. 114 */ 115#define STRSIZEOF(x) (sizeof(x)-1) 116static void 117collect_info(int fd) 118{ 119 char *line; 120 FILE *fp; 121 char *ptr; 122 size_t len; 123 int line_count = 0; 124 125 fp = fdopen(fd, "r"); 126 127 while ((line = fgetln(fp, &len)) != NULL) { 128 if (++line_count > AUTHMAXLINES) 129 break; 130 if (len && line[len-1] == '\n') 131 --len; 132 line[len] = '\0'; /* Terminate */ 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 return 0; 289} 290 291 292/* 293 * auth_scan() 294 * Goes through the output of the auth_script/authenticate, and 295 * checks for a failure or authentication. 296 * <ok> is a default authentication value -- if there are no 297 * rejection or authentication statements, then it is returned 298 * unmodified. 299 * AUTH_NONE is returned if there were any reject statements 300 * from the authentication program (invoked by auth_script()), and 301 * AUTH, AUTH_ROOTOKAY, and/or AUTH_SECURE are returned if the 302 * appropriate directives were found. Note that AUTH* are 303 * *bitmasks*! 304 */ 305 306int 307auth_scan(int ok) 308{ 309 if (auth_info.reject) 310 return 0; 311 return ok | auth_info.auths; 312} 313 314 315/* 316 * auth_rmfiles() 317 * Removes any files that the authentication program said needed to be 318 * removed, said files having come from a previous execution of 319 * auth_script(). 320 */ 321 322int 323auth_rmfiles(void) 324{ 325 int i = auth_info.file_count; 326 while (i-- > 0) { 327 unlink(auth_info.files[i]); 328 free(auth_info.files[i]); 329 auth_info.files[i] = NULL; 330 } 331 return 0; 332} 333 334 335/* 336 * auth_checknologin() 337 * Checks for the existance of a nologin file in the login_cap 338 * capability <lc>. If there isn't one specified, then it checks 339 * to see if this class should just ignore nologin files. Lastly, 340 * it tries to print out the default nologin file, and, if such 341 * exists, it exits. 342 */ 343 344void 345auth_checknologin(login_cap_t *lc) 346{ 347 char *file; 348 349 /* Do we ignore a nologin file? */ 350 if (login_getcapbool(lc, "ignorenologin", 0)) 351 return; 352 353 /* Note that <file> will be "" if there is no nologin capability */ 354 if ((file = login_getcapstr(lc, "nologin", "", NULL)) == NULL) 355 exit(1); 356 357 /* 358 * *file is true IFF there was a "nologin" capability 359 * Note that auth_cat() returns 1 only if the specified 360 * file exists, and is readable. E.g., /.nologin exists. 361 */ 362 if ((*file && auth_cat(file)) || auth_cat(_PATH_NOLOGIN)) 363 exit(1); 364} 365 366 367/* 368 * auth_cat() 369 * Checks for the readability of <file>; if it can be opened for 370 * reading, it prints it out to stdout, and then exits. Otherwise, 371 * it returns 0 (meaning no nologin file). 372 */ 373int 374auth_cat(const char *file) 375{ 376 int fd, count; 377 char buf[BUFSIZ]; 378 379 if ((fd = open(file, O_RDONLY)) < 0) 380 return 0; 381 while ((count = read(fd, buf, sizeof(buf))) > 0) 382 write(fileno(stdout), buf, count); 383 close(fd); 384 return 1; 385} 386