login_auth.c revision 22086
11556Srgrimes/*- 21556Srgrimes * Copyright (c) 1996 by 31556Srgrimes * Sean Eric Fagan <sef@kithrup.com> 41556Srgrimes * David Nugent <davidn@blaze.net.au> 51556Srgrimes * All rights reserved. 61556Srgrimes * 71556Srgrimes * Redistribution and use in source and binary forms, with or without 81556Srgrimes * modification, is permitted provided that the following conditions 91556Srgrimes * are met: 101556Srgrimes * 1. Redistributions of source code must retain the above copyright 111556Srgrimes * notice immediately at the beginning of the file, without modification, 121556Srgrimes * this list of conditions, and the following disclaimer. 131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141556Srgrimes * notice, this list of conditions and the following disclaimer in the 151556Srgrimes * documentation and/or other materials provided with the distribution. 161556Srgrimes * 3. This work was done expressly for inclusion into FreeBSD. Other use 171556Srgrimes * is permitted provided this notation is included. 181556Srgrimes * 4. Absolutely no warranty of function or purpose is made by the authors. 191556Srgrimes * 5. Modifications may be freely made to this file providing the above 201556Srgrimes * conditions are met. 211556Srgrimes * 221556Srgrimes * Low-level routines relating to the user capabilities database 231556Srgrimes * 241556Srgrimes * $FreeBSD: head/lib/libutil/login_auth.c 22086 1997-01-29 06:11:31Z davidn $ 251556Srgrimes */ 261556Srgrimes 271556Srgrimes#include <sys/types.h> 281556Srgrimes#include <sys/time.h> 291556Srgrimes#include <sys/resource.h> 301556Srgrimes#include <sys/stat.h> 311556Srgrimes#include <errno.h> 321556Srgrimes#include <fcntl.h> 331556Srgrimes#include <limits.h> 341556Srgrimes#include <stdio.h> 351556Srgrimes#include <pwd.h> 361556Srgrimes#include <stdlib.h> 371556Srgrimes#include <string.h> 381556Srgrimes#include <syslog.h> 3935773Scharnier#include <unistd.h> 4036007Scharnier#include <login_cap.h> 4135773Scharnier#include <stdarg.h> 4235773Scharnier#include <paths.h> 4350471Speter#include <sys/wait.h> 441556Srgrimes 451556Srgrimesextern char *fgetline(FILE *, int*); 4636007Scharnier 471556Srgrimes#ifdef RLIM_LONG 481556Srgrimes# define STRTOV strtol 491556Srgrimes#else 5051137Sgreen# define STRTOV strtoq 511556Srgrimes#endif 521556Srgrimes 531556Srgrimes#define AUTHMAXLINES 1024 541556Srgrimes#define AUTHMAXARGS 16 551556Srgrimes 561556Srgrimesstruct auth_info { 571556Srgrimes int reject; 581556Srgrimes int auths; 591556Srgrimes int env_count; 601556Srgrimes char **env; 611556Srgrimes int file_count; 621556Srgrimes char **files; 631556Srgrimes}; 641556Srgrimes 6551208Sgreenstatic struct auth_info auth_info; 6651208Sgreen 6748026Sgreen/* 6851208Sgreen * free_auth_info() 691556Srgrimes * Go through the auth_info structure, and free() anything of interest. 7051249Sgreen * This includes the string arrays, and any individual element. 7151249Sgreen * All part of being environmentally conscious ;). 7251137Sgreen */ 7351208Sgreen 7451208Sgreenstatic void 751556Srgrimesfree_auth_info(void) 761556Srgrimes{ 771556Srgrimes int i; 781556Srgrimes 791556Srgrimes auth_info.reject = 0; 801556Srgrimes auth_info.auths = 0; 811556Srgrimes if (auth_info.env) { 821556Srgrimes for (i = 0; i < auth_info.env_count; i++) { 831556Srgrimes if (auth_info.env[i]) 841556Srgrimes free(auth_info.env[i]); 851556Srgrimes } 861556Srgrimes free(auth_info.env); 871556Srgrimes auth_info.env = NULL; 881556Srgrimes } 891556Srgrimes if (auth_info.files) { 901556Srgrimes for (i = 0; i < auth_info.file_count; i++) { 911556Srgrimes if (auth_info.files[i]) 921556Srgrimes free(auth_info.files[i]); 931556Srgrimes } 941556Srgrimes free(auth_info.files); 951556Srgrimes auth_info.files = NULL; 961556Srgrimes } 971556Srgrimes} 981556Srgrimes 991556Srgrimes 1001556Srgrimes/* 1011556Srgrimes * collect_info() 1021556Srgrimes * Read from <fd>, a list of authorization commands. 1031556Srgrimes * These commands are: 1041556Srgrimes * reject 1051556Srgrimes * authorize [root|secure] 1061556Srgrimes * setenv <name>[ <value>] 1071556Srgrimes * remove <file> 1081556Srgrimes * A single reject means the entire thing is bad; 1091556Srgrimes * multiple authorize statements can be present (it would be 1101556Srgrimes * silly, but that's what the spec says). 1111556Srgrimes * The commands are collected, and are accted upon by: 1121556Srgrimes * auth_scan() -- check for authorization or rejection 1131556Srgrimes * auth_rmfiles() -- remove the specified files 1141556Srgrimes * auth_env() -- set the specified environment variables 1151556Srgrimes * We only get up to AUTHMAXLINES lines of input from the program. 1161556Srgrimes */ 1171556Srgrimes#define STRSIZEOF(x) (sizeof(x)-1) 1181556Srgrimesstatic void 1191556Srgrimescollect_info(int fd) 1201556Srgrimes{ 1211556Srgrimes char *line; 1221556Srgrimes FILE *fp; 1231556Srgrimes char *ptr; 1241556Srgrimes int len; 12548026Sgreen int line_count = 0; 12648026Sgreen 1271556Srgrimes fp = fdopen(fd, "r"); 12851249Sgreen 12951249Sgreen while ((line = fgetline(fp, &len)) != NULL) { 13051249Sgreen if (++line_count > AUTHMAXLINES) 13151249Sgreen break; 13251249Sgreen if (strncasecmp(line, BI_REJECT, STRSIZEOF(BI_REJECT)) == 0) { 13362311Sgreen auth_info.reject = 1; 13451137Sgreen } else if (strncasecmp(line, BI_AUTH, STRSIZEOF(BI_AUTH)) == 0) { 13551335Sgreen ptr = line + STRSIZEOF(BI_AUTH); 13651208Sgreen ptr += strspn(ptr, " \t"); 1371556Srgrimes if (!*ptr) 1381556Srgrimes auth_info.auths |= AUTH_OKAY; 1391556Srgrimes else if (strncasecmp(ptr, BI_ROOTOKAY, STRSIZEOF(BI_ROOTOKAY)) == 0) 1401556Srgrimes auth_info.auths |= AUTH_ROOTOKAY; 1411556Srgrimes else if (strncasecmp(ptr, BI_SECURE, STRSIZEOF(BI_SECURE)) == 0) 1421556Srgrimes auth_info.auths |= AUTH_SECURE; 1431556Srgrimes } else if (strncasecmp(line, BI_SETENV, STRSIZEOF(BI_SETENV)) == 0) { 1441556Srgrimes ptr = line + STRSIZEOF(BI_SETENV); 1451556Srgrimes ptr += strspn(ptr, " \t"); 14648026Sgreen if (*ptr) { 1471556Srgrimes char **tmp = realloc(auth_info.env, sizeof(char*) * (auth_info.env_count + 1)); 1481556Srgrimes if (tmp != NULL) { 1491556Srgrimes auth_info.env = tmp; 1501556Srgrimes if ((auth_info.env[auth_info.env_count] = strdup(ptr)) != NULL) 1511556Srgrimes auth_info.env_count++; 1521556Srgrimes } 1531556Srgrimes } 1541556Srgrimes } else if (strncasecmp(line, BI_REMOVE, STRSIZEOF(BI_REMOVE)) == 0) { 1551556Srgrimes ptr = line + STRSIZEOF(BI_REMOVE); 15648026Sgreen ptr += strspn(ptr, " \t"); 1571556Srgrimes if (*ptr) { 1581556Srgrimes char **tmp = realloc(auth_info.files, sizeof(char*) * (auth_info.file_count + 1)); 1591556Srgrimes if (tmp != NULL) { 1601556Srgrimes auth_info.files = tmp; 1611556Srgrimes if ((auth_info.files[auth_info.file_count] = strdup(ptr)) != NULL) 1621556Srgrimes auth_info.file_count++; 1631556Srgrimes } 1641556Srgrimes } 1651556Srgrimes } 1661556Srgrimes } 1671556Srgrimes fclose(fp); 1681556Srgrimes} 16962311Sgreen 17062311Sgreen 17162311Sgreen/* 1721556Srgrimes * authenticate() 17362311Sgreen * Starts an auth_script() for the given <user>, with a class <class>, 17462311Sgreen * style <style>, and service <service>. <style> is necessary, 17562311Sgreen * as are <user> and <class>, but <service> is optional -- it defaults 1761556Srgrimes * to "login". 1771556Srgrimes * Since auth_script() expects an execl'able program name, authenticate() 1781556Srgrimes * 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