authenticate.c revision 1.19
1/* $OpenBSD: authenticate.c,v 1.19 2013/09/30 12:02:32 millert Exp $ */ 2 3/*- 4 * Copyright (c) 1997 Berkeley Software Design, Inc. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by Berkeley Software Design, 17 * Inc. 18 * 4. The name of Berkeley Software Design, Inc. may not be used to endorse 19 * or promote products derived from this software without specific prior 20 * written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * BSDI $From: authenticate.c,v 2.21 1999/09/08 22:33:26 prb Exp $ 35 */ 36 37#include <sys/stat.h> 38 39#include <ctype.h> 40#include <err.h> 41#include <fcntl.h> 42#include <limits.h> 43#include <login_cap.h> 44#include <paths.h> 45#include <pwd.h> 46#include <stdarg.h> 47#include <stdio.h> 48#include <stdlib.h> 49#include <string.h> 50#include <syslog.h> 51#include <unistd.h> 52 53#include <bsd_auth.h> 54 55static int _auth_checknologin(login_cap_t *, int); 56 57char * 58auth_mkvalue(char *value) 59{ 60 char *big, *p; 61 62 big = malloc(strlen(value) * 4 + 1); 63 if (big == NULL) 64 return (NULL); 65 /* 66 * XXX - There should be a more standardized 67 * routine for doing this sort of thing. 68 */ 69 for (p = big; *value; ++value) { 70 switch (*value) { 71 case '\r': 72 *p++ = '\\'; 73 *p++ = 'r'; 74 break; 75 case '\n': 76 *p++ = '\\'; 77 *p++ = 'n'; 78 break; 79 case '\\': 80 *p++ = '\\'; 81 *p++ = *value; 82 break; 83 case '\t': 84 case ' ': 85 if (p == big) 86 *p++ = '\\'; 87 *p++ = *value; 88 break; 89 default: 90 if (!isprint(*value)) { 91 *p++ = '\\'; 92 *p++ = ((*value >> 6) & 0x3) + '0'; 93 *p++ = ((*value >> 3) & 0x7) + '0'; 94 *p++ = ((*value ) & 0x7) + '0'; 95 } else 96 *p++ = *value; 97 break; 98 } 99 } 100 *p = '\0'; 101 return (big); 102} 103 104void 105auth_checknologin(login_cap_t *lc) 106{ 107 if (_auth_checknologin(lc, 1)) 108 exit(1); 109} 110 111static int 112_auth_checknologin(login_cap_t *lc, int print) 113{ 114 struct stat sb; 115 char *nologin; 116 int mustfree; 117 118 if (login_getcapbool(lc, "ignorenologin", 0)) 119 return (0); 120 121 /* 122 * If we fail to get the nologin file due to a database error, 123 * assume there should have been one... 124 */ 125 nologin = login_getcapstr(lc, "nologin", "", NULL); 126 mustfree = nologin && *nologin != '\0'; 127 if (nologin == NULL) 128 goto print_nologin; 129 130 /* First try the nologin file specified in login.conf. */ 131 if (*nologin != '\0' && stat(nologin, &sb) == 0) 132 goto print_nologin; 133 if (mustfree) { 134 free(nologin); 135 mustfree = 0; 136 } 137 138 /* If that doesn't exist try _PATH_NOLOGIN. */ 139 if (stat(_PATH_NOLOGIN, &sb) == 0) { 140 nologin = _PATH_NOLOGIN; 141 goto print_nologin; 142 } 143 144 /* Couldn't stat any nologin files, must be OK to login. */ 145 return (0); 146 147print_nologin: 148 if (print) { 149 if (!nologin || *nologin == '\0' || auth_cat(nologin) == 0) { 150 puts("Logins are not allowed at this time."); 151 fflush(stdout); 152 } 153 } 154 if (mustfree) 155 free(nologin); 156 return (-1); 157} 158 159int 160auth_cat(char *file) 161{ 162 int fd, nchars; 163 char tbuf[8192]; 164 165 if ((fd = open(file, O_RDONLY, 0)) < 0) 166 return (0); 167 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) 168 (void)write(fileno(stdout), tbuf, nchars); 169 (void)close(fd); 170 return (1); 171} 172 173int 174auth_approval(auth_session_t *as, login_cap_t *lc, char *name, char *type) 175{ 176 int close_on_exit, close_lc_on_exit, len; 177 struct passwd *pwd; 178 char *approve, *s, path[PATH_MAX]; 179 180 pwd = NULL; 181 close_on_exit = as == NULL; 182 close_lc_on_exit = lc == NULL; 183 184 if (as != NULL && name == NULL) 185 name = auth_getitem(as, AUTHV_NAME); 186 187 if (as != NULL) 188 pwd = auth_getpwd(as); 189 190 if (pwd == NULL) { 191 if (name != NULL) 192 pwd = getpwnam(name); 193 else { 194 if ((pwd = getpwuid(getuid())) == NULL) { 195 syslog(LOG_ERR, "no such user id %u", getuid()); 196 _warnx("cannot approve who we don't recognize"); 197 return (0); 198 } 199 name = pwd->pw_name; 200 } 201 } 202 203 if (name == NULL) 204 name = pwd->pw_name; 205 206 if (lc == NULL) { 207 if (strlen(name) >= PATH_MAX) { 208 syslog(LOG_ERR, "username to login %.*s...", 209 PATH_MAX, name); 210 _warnx("username too long"); 211 return (0); 212 } 213 if (pwd == NULL && (approve = strchr(name, '.')) != NULL) { 214 strlcpy(path, name, sizeof path); 215 path[approve-name] = '\0'; 216 pwd = getpwnam(name); 217 } 218 lc = login_getclass(pwd ? pwd->pw_class : NULL); 219 if (lc == NULL) { 220 _warnx("unable to classify user"); 221 return (0); 222 } 223 } 224 225 if (!type) 226 type = LOGIN_DEFSERVICE; 227 else { 228 if (strncmp(type, "approve-", 8) == 0) 229 type += 8; 230 231 len = snprintf(path, sizeof(path), "approve-%s", type); 232 if (len < 0 || len >= sizeof(path)) { 233 if (close_lc_on_exit) 234 login_close(lc); 235 syslog(LOG_ERR, "approval path too long %.*s...", 236 PATH_MAX, type); 237 _warnx("approval script path too long"); 238 return (0); 239 } 240 } 241 242 if ((approve = login_getcapstr(lc, s = path, NULL, NULL)) == NULL) 243 approve = login_getcapstr(lc, s = "approve", NULL, NULL); 244 245 if (approve && approve[0] != '/') { 246 if (close_lc_on_exit) 247 login_close(lc); 248 syslog(LOG_ERR, "Invalid %s script: %s", s, approve); 249 _warnx("invalid path to approval script"); 250 free(approve); 251 return (0); 252 } 253 254 if (as == NULL && (as = auth_open()) == NULL) { 255 if (close_lc_on_exit) 256 login_close(lc); 257 syslog(LOG_ERR, "%m"); 258 _warn(NULL); 259 if (approve) 260 free(approve); 261 return (0); 262 } 263 264 auth_setstate(as, AUTH_OKAY); 265 if (auth_setitem(as, AUTHV_NAME, name) < 0) { 266 syslog(LOG_ERR, "%m"); 267 _warn(NULL); 268 goto out; 269 } 270 if (auth_check_expire(as) < 0) /* is this account expired */ 271 goto out; 272 if (_auth_checknologin(lc, 273 auth_getitem(as, AUTHV_INTERACTIVE) != NULL)) { 274 auth_setstate(as, (auth_getstate(as) & ~AUTH_ALLOW)); 275 goto out; 276 } 277 if (login_getcapbool(lc, "requirehome", 0) && pwd && pwd->pw_dir && 278 pwd->pw_dir[0]) { 279 struct stat sb; 280 281 if (stat(pwd->pw_dir, &sb) < 0 || 282 (sb.st_mode & 0170000) != S_IFDIR || 283 (pwd->pw_uid && sb.st_uid == pwd->pw_uid && 284 (sb.st_mode & S_IXUSR) == 0)) { 285 auth_setstate(as, (auth_getstate(as) & ~AUTH_ALLOW)); 286 goto out; 287 } 288 } 289 if (approve) 290 auth_call(as, approve, strrchr(approve, '/') + 1, name, 291 lc->lc_class, type, (char *)NULL); 292 293out: 294 if (approve) 295 free(approve); 296 if (close_lc_on_exit) 297 login_close(lc); 298 299 if (close_on_exit) 300 return (auth_close(as)); 301 return (auth_getstate(as) & AUTH_ALLOW); 302} 303 304auth_session_t * 305auth_usercheck(char *name, char *style, char *type, char *password) 306{ 307 char namebuf[LOGIN_NAME_MAX + 1 + NAME_MAX + 1]; 308 auth_session_t *as; 309 login_cap_t *lc; 310 struct passwd *pwd; 311 char *slash; 312 313 if (strlcpy(namebuf, name, sizeof(namebuf)) >= sizeof(namebuf)) 314 return (NULL); 315 name = namebuf; 316 317 /* 318 * Split up user:style names if we were not given a style 319 */ 320 if (style == NULL && (style = strchr(name, ':')) != NULL) 321 *style++ = '\0'; 322 323 /* 324 * Cope with user/instance. We are only using this to get 325 * the class so it is okay if we strip a /root instance 326 * The actual login script will pay attention to the instance. 327 */ 328 if ((pwd = getpwnam(name)) == NULL) { 329 if ((slash = strchr(name, '/')) != NULL) { 330 *slash = '\0'; 331 pwd = getpwnam(name); 332 *slash = '/'; 333 } 334 } 335 if ((lc = login_getclass(pwd ? pwd->pw_class : NULL)) == NULL) 336 return (NULL); 337 338 if ((style = login_getstyle(lc, style, type)) == NULL) { 339 login_close(lc); 340 return (NULL); 341 } 342 343 if (password) { 344 if ((as = auth_open()) == NULL) { 345 login_close(lc); 346 return (NULL); 347 } 348 auth_setitem(as, AUTHV_SERVICE, "response"); 349 auth_setdata(as, "", 1); 350 auth_setdata(as, password, strlen(password) + 1); 351 memset(password, 0, strlen(password)); 352 } else 353 as = NULL; 354 as = auth_verify(as, style, name, lc->lc_class, (char *)NULL); 355 login_close(lc); 356 return (as); 357} 358 359int 360auth_userokay(char *name, char *style, char *type, char *password) 361{ 362 auth_session_t *as; 363 364 as = auth_usercheck(name, style, type, password); 365 366 return (as != NULL ? auth_close(as) : 0); 367} 368 369auth_session_t * 370auth_userchallenge(char *name, char *style, char *type, char **challengep) 371{ 372 char namebuf[LOGIN_NAME_MAX + 1 + NAME_MAX + 1]; 373 auth_session_t *as; 374 login_cap_t *lc; 375 struct passwd *pwd; 376 char *slash; 377 378 if (strlen(name) >= sizeof(namebuf)) 379 return (NULL); 380 strlcpy(namebuf, name, sizeof namebuf); 381 name = namebuf; 382 383 /* 384 * Split up user:style names if we were not given a style 385 */ 386 if (style == NULL && (style = strchr(name, ':')) != NULL) 387 *style++ = '\0'; 388 389 /* 390 * Cope with user/instance. We are only using this to get 391 * the class so it is okay if we strip a /root instance 392 * The actual login script will pay attention to the instance. 393 */ 394 if ((pwd = getpwnam(name)) == NULL) { 395 if ((slash = strchr(name, '/')) != NULL) { 396 *slash = '\0'; 397 pwd = getpwnam(name); 398 *slash = '/'; 399 } 400 } 401 if ((lc = login_getclass(pwd ? pwd->pw_class : NULL)) == NULL) 402 return (NULL); 403 404 if ((style = login_getstyle(lc, style, type)) == NULL || 405 (as = auth_open()) == NULL) { 406 login_close(lc); 407 return (NULL); 408 } 409 if (auth_setitem(as, AUTHV_STYLE, style) < 0 || 410 auth_setitem(as, AUTHV_NAME, name) < 0 || 411 auth_setitem(as, AUTHV_CLASS, lc->lc_class) < 0) { 412 auth_close(as); 413 login_close(lc); 414 return (NULL); 415 } 416 login_close(lc); 417 *challengep = auth_challenge(as); 418 return (as); 419} 420 421int 422auth_userresponse(auth_session_t *as, char *response, int more) 423{ 424 char path[PATH_MAX]; 425 char *style, *name, *challenge, *class; 426 int len; 427 428 if (as == NULL) 429 return (0); 430 431 auth_setstate(as, 0); 432 433 if ((style = auth_getitem(as, AUTHV_STYLE)) == NULL || 434 (name = auth_getitem(as, AUTHV_NAME)) == NULL) { 435 if (more == 0) 436 return (auth_close(as)); 437 return(0); 438 } 439 440 len = snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", style); 441 if (len < 0 || len >= sizeof(path)) { 442 if (more == 0) 443 return (auth_close(as)); 444 return (0); 445 } 446 447 challenge = auth_getitem(as, AUTHV_CHALLENGE); 448 class = auth_getitem(as, AUTHV_CLASS); 449 450 if (challenge) 451 auth_setdata(as, challenge, strlen(challenge) + 1); 452 else 453 auth_setdata(as, "", 1); 454 if (response) { 455 auth_setdata(as, response, strlen(response) + 1); 456 memset(response, 0, strlen(response)); 457 } else 458 auth_setdata(as, "", 1); 459 460 auth_call(as, path, style, "-s", "response", name, class, (char *)NULL); 461 462 /* 463 * If they authenticated then make sure they did not expire 464 */ 465 if (auth_getstate(as) & AUTH_ALLOW) 466 auth_check_expire(as); 467 if (more == 0) 468 return (auth_close(as)); 469 return (auth_getstate(as) & AUTH_ALLOW); 470} 471 472/* 473 * Authenticate name with the specified style. 474 * If ``as'' is NULL a new session is formed with the default service. 475 * Returns NULL only if ``as'' is NULL and we were unable to allocate 476 * a new session. 477 * 478 * Use auth_close() or auth_getstate() to determine if the authentication 479 * worked. 480 */ 481auth_session_t * 482auth_verify(auth_session_t *as, char *style, char *name, ...) 483{ 484 va_list ap; 485 char path[PATH_MAX]; 486 487 if ((name == NULL || style == NULL) && as == NULL) 488 return (as); 489 490 if (as == NULL && (as = auth_open()) == NULL) 491 return (NULL); 492 auth_setstate(as, 0); 493 494 if (style != NULL && auth_setitem(as, AUTHV_STYLE, style) < 0) 495 return (as); 496 497 if (name != NULL && auth_setitem(as, AUTHV_NAME, name) < 0) 498 return (as); 499 500 style = auth_getitem(as, AUTHV_STYLE); 501 name = auth_getitem(as, AUTHV_NAME); 502 503 snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", style); 504 va_start(ap, name); 505 auth_set_va_list(as, ap); 506 auth_call(as, path, auth_getitem(as, AUTHV_STYLE), "-s", 507 auth_getitem(as, AUTHV_SERVICE), name, (char *)NULL); 508 va_end(ap); 509 return (as); 510} 511