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$ 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 54 struct auth_info { 55 int reject; 56 int auths; 57 int env_count; 58 char **env; 59 int file_count; 60 char **files; 61 }; 62 63 static 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 72 static void 73 free_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) 116 static void 117 collect_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 */ 186 int 187 authenticate(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 230 int 231 auth_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 274 int 275 auth_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 306 int 307 auth_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 322 int 323 auth_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 344 void 345 auth_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 */ 373 int 374 auth_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