| 1 | /* |
| 2 | * Copyright 2000, International Business Machines Corporation and others. |
| 3 | * All Rights Reserved. |
| 4 | * |
| 5 | * This software has been released under the terms of the IBM Public |
| 6 | * License. For details, see the LICENSE file in the top-level source |
| 7 | * directory or online at http://www.openafs.org/dl/license10.html |
| 8 | */ |
| 9 | |
| 10 | /* |
| 11 | * Implements most of the client side web authentication protocol |
| 12 | */ |
| 13 | |
| 14 | /* |
| 15 | */ |
| 16 | |
| 17 | #include "apache_afs_utils.h" |
| 18 | #include "apache_afs_cache.h" |
| 19 | |
| 20 | #include "AFS_component_version_number.c" |
| 21 | #include "apache_api.h" |
| 22 | |
| 23 | #define afsassert(str) if(!(str)) { fprintf(stderr, "afs module: assertion failed:%s\t%d\n",__FILE__,__LINE__) ; return SERVER_ERROR; } |
| 24 | |
| 25 | #define MAXBUFF 1024 |
| 26 | |
| 27 | #define APACHEAFS_MAX_PATH 1024 /* Maximum path length */ |
| 28 | #define APACHEAFS_USERNAME_MAX 64 /* Maximum username length */ |
| 29 | #define APACHEAFS_PASSWORD_MAX 64 /* Maximum password length */ |
| 30 | #define APACHEAFS_CELLNAME_MAX 64 /* Maximum cellname length */ |
| 31 | #define APACHEAFS_MOUNTPTLEN_MAX 64 /* Max mountpoint length */ |
| 32 | |
| 33 | #ifndef MAX |
| 34 | #define MAX(A,B) ((A)>(B)?(A):(B)) |
| 35 | #endif /* !MAX */ |
| 36 | #ifndef MIN |
| 37 | #define MIN(A,B) ((A)<(B)?(A):(B)) |
| 38 | #endif /* !MAX */ |
| 39 | |
| 40 | /* */ |
| 41 | static int setCellAuthHeader(request_rec * r); |
| 42 | |
| 43 | /* Module name for logging stuff */ |
| 44 | extern const char module_name[]; |
| 45 | |
| 46 | /* file descriptors for the pipes for communication with weblog */ |
| 47 | extern int readPipe; |
| 48 | extern int writePipe; |
| 49 | |
| 50 | #ifdef AIX |
| 51 | /* lock file descriptor */ |
| 52 | extern int tempfd; |
| 53 | #endif |
| 54 | |
| 55 | /* do we have an authentication field */ |
| 56 | static int haveAuth = 0; |
| 57 | |
| 58 | /* local cache stuff */ |
| 59 | static int cache_initialized; |
| 60 | |
| 61 | /* we need the defult cell in several places */ |
| 62 | static char global_default_cell[APACHEAFS_CELLNAME_MAX]; |
| 63 | |
| 64 | /* buffers to keep track of the last authenticated user */ |
| 65 | static char lastUser[APACHEAFS_USERNAME_MAX]; |
| 66 | static char lastCell[APACHEAFS_CELLNAME_MAX]; |
| 67 | static char lastCksum[SHA_HASH_BYTES]; |
| 68 | |
| 69 | /* do I have my own PAG */ |
| 70 | static int doneSETPAG = 0; |
| 71 | |
| 72 | /* |
| 73 | * Parse the authorization header for username and password |
| 74 | */ |
| 75 | static int |
| 76 | parse_authhdr(request_rec * r, char *user, char *passwd, char *cell, |
| 77 | char *defaultCell) |
| 78 | { |
| 79 | int i; |
| 80 | char *p, *t; |
| 81 | const char *auth_line = TABLE_GET(r->headers_in, "Authorization"); |
| 82 | |
| 83 | if ((r == NULL) || (auth_line == NULL) || (user == NULL) |
| 84 | || (passwd == NULL) || (cell == NULL)) { |
| 85 | LOG_REASON("AFSAUTH_CLIENT: NULL request record, auth_line, cell," |
| 86 | "user or passwd while parsing authentication header", |
| 87 | r->uri, r); |
| 88 | return SERVER_ERROR; |
| 89 | } |
| 90 | |
| 91 | user[0] = '\0'; |
| 92 | passwd[0] = '\0'; |
| 93 | cell[0] = '\0'; |
| 94 | |
| 95 | /* |
| 96 | * check for basic authentication |
| 97 | */ |
| 98 | if (strncasecmp |
| 99 | (GETWORD(r->pool, (const char **)&auth_line, ' '), "basic", 6) != 0) { |
| 100 | /* Client tried to authenticate using some other auth scheme */ |
| 101 | LOG_REASON |
| 102 | ("AFSAUTH_CLIENT:client used other than Basic authentication" |
| 103 | "scheme", r->uri, r); |
| 104 | return SERVER_ERROR; |
| 105 | } |
| 106 | |
| 107 | /* |
| 108 | * Username and password are base64 encoded |
| 109 | */ |
| 110 | t = UUDECODE(r->pool, auth_line); |
| 111 | |
| 112 | if (t == NULL) { |
| 113 | LOG_REASON("AFSAUTH_CLIENT:uudecode failed", r->uri, r); |
| 114 | return SERVER_ERROR; |
| 115 | } |
| 116 | |
| 117 | /* |
| 118 | * Format is user@cell:passwd. The user, cell or passwd may be missing |
| 119 | */ |
| 120 | r->connection->user = GETWORD_NULLS(r->pool, (const char **)&t, ':'); |
| 121 | r->connection->auth_type = "Basic"; |
| 122 | strcpy(passwd, t); |
| 123 | |
| 124 | p = r->connection->user; |
| 125 | |
| 126 | for (i = 0; *p != '@' && *p != '\0'; p++, i++) { |
| 127 | user[i] = *p; |
| 128 | } |
| 129 | user[i] = '\0'; |
| 130 | if (*p == '@') { |
| 131 | for (i = 0, p++; *p != '\0'; p++, i++) { |
| 132 | cell[i] = *p; |
| 133 | } |
| 134 | cell[i] = '\0'; |
| 135 | } |
| 136 | |
| 137 | if (cell[0] == '\0') { |
| 138 | strcpy(cell, defaultCell); |
| 139 | } |
| 140 | return OK; |
| 141 | } |
| 142 | |
| 143 | /* |
| 144 | * send a buffer to the weblog process over the pipe. Used for sending |
| 145 | * authentication credentials to weblog |
| 146 | */ |
| 147 | static int |
| 148 | sendBuffer(char *buf, int len) |
| 149 | { |
| 150 | afsassert(buf); |
| 151 | if (write(writePipe, buf, len) != len) { |
| 152 | afslog(5, |
| 153 | ("%s: Error writing to pipe - %s", module_name, |
| 154 | strerror(errno))); |
| 155 | return -1; |
| 156 | } |
| 157 | return 0; |
| 158 | } |
| 159 | |
| 160 | /* |
| 161 | * packs user credentials into a buffer seperated by newlines and |
| 162 | * sends them to weblog |
| 163 | */ |
| 164 | static int |
| 165 | sendTo_afsAuthenticator(char *user, char *passwd, char *cell, char *type) |
| 166 | { |
| 167 | char buf[MAXBUFF]; |
| 168 | |
| 169 | afsassert(user); |
| 170 | afsassert(passwd); |
| 171 | afsassert(cell); |
| 172 | afsassert(type); |
| 173 | |
| 174 | sprintf(buf, "%s\n%s\n%s\n%s", type, user, cell, passwd); |
| 175 | return sendBuffer(buf, strlen(buf)); |
| 176 | } |
| 177 | |
| 178 | /* |
| 179 | * reads the response from weblog over the pipe |
| 180 | */ |
| 181 | static int |
| 182 | recvFrom_afsAuthenticator(char *buf) |
| 183 | { |
| 184 | int n; |
| 185 | |
| 186 | afsassert(buf); |
| 187 | n = read(readPipe, buf, MAXBUFF); |
| 188 | if (n < 0) { |
| 189 | afslog(5, |
| 190 | ("%s: Error reading from pipe - %s", module_name, |
| 191 | strerror(errno))); |
| 192 | return -1; |
| 193 | } |
| 194 | return n; |
| 195 | } |
| 196 | |
| 197 | #ifndef NO_AFSAPACHE_CACHE |
| 198 | /* |
| 199 | * check local cache for the token associated with user crds. |
| 200 | */ |
| 201 | static int |
| 202 | check_Cache(char *user, char *passwd, char *cell, char *tokenBuf) |
| 203 | { |
| 204 | char cksum[SHA_HASH_BYTES]; /* for sha checksum for caching */ |
| 205 | |
| 206 | /* look up local cache - function in apache_afs_cach.c */ |
| 207 | weblog_login_checksum(user, cell, passwd, cksum); |
| 208 | return weblog_login_lookup(user, cell, cksum, &tokenBuf[0]); |
| 209 | } |
| 210 | |
| 211 | /* |
| 212 | * put the token and the user credentials in the local cache |
| 213 | */ |
| 214 | static int |
| 215 | updateCache(char *user, char *passwd, char *cell, char *tokenBuf, |
| 216 | int cacheExpiration) |
| 217 | { |
| 218 | long expires = 0, testExpires = 0; |
| 219 | char cksum[SHA_HASH_BYTES]; /* for sha checksum for caching */ |
| 220 | |
| 221 | /* put the token in local cache with the expiration date */ |
| 222 | expires = getExpiration(tokenBuf); |
| 223 | if (expires < 0) { |
| 224 | afslog(5, |
| 225 | ("%s: Error getting expiration time for cache. Expires %d", |
| 226 | module_name, expires)); |
| 227 | return -1; |
| 228 | } |
| 229 | |
| 230 | weblog_login_checksum(user, cell, passwd, cksum); |
| 231 | |
| 232 | if (cacheExpiration == 0) { |
| 233 | weblog_login_store(user, cell, cksum, &tokenBuf[0], sizeof(tokenBuf), |
| 234 | expires); |
| 235 | } else { |
| 236 | testExpires = cacheExpiration + time(NULL); |
| 237 | weblog_login_store(user, cell, cksum, &tokenBuf[0], sizeof(tokenBuf), |
| 238 | MIN(expires, testExpires)); |
| 239 | } |
| 240 | return 0; |
| 241 | } |
| 242 | #endif /* NO_APACHEAFS_CACHE */ |
| 243 | |
| 244 | |
| 245 | /* |
| 246 | * locking routines to provide exclusive access to the pipes |
| 247 | */ |
| 248 | static int |
| 249 | start_lock(int fd, int cmd, int type) |
| 250 | { |
| 251 | struct flock lock; |
| 252 | lock.l_type = type; |
| 253 | lock.l_start = 0; |
| 254 | lock.l_whence = SEEK_SET; |
| 255 | lock.l_len = 0; |
| 256 | return (fcntl(fd, cmd, &lock)); |
| 257 | } |
| 258 | |
| 259 | static int |
| 260 | test_lock(int fd, int type) |
| 261 | { |
| 262 | struct flock lock; |
| 263 | lock.l_type = type; |
| 264 | lock.l_start = 0; |
| 265 | lock.l_whence = SEEK_SET; |
| 266 | lock.l_len = 0; |
| 267 | |
| 268 | if (fcntl(fd, F_GETLK, &lock) < 0) { |
| 269 | return -1; |
| 270 | } |
| 271 | if (lock.l_type == F_UNLCK) { |
| 272 | return 0; /* not locked */ |
| 273 | } |
| 274 | return (lock.l_pid); /* return pid of locking process */ |
| 275 | } |
| 276 | |
| 277 | |
| 278 | #define Read_lock(fd) \ |
| 279 | start_lock(fd, F_SETLK, F_RDLCK) |
| 280 | #define Readw_lock(fd) \ |
| 281 | start_lock(fd, F_SETLKW, F_RDLCK) |
| 282 | #define Write_lock(fd) \ |
| 283 | start_lock(fd, F_SETLK, F_WRLCK) |
| 284 | #define Writew_lock(fd) \ |
| 285 | start_lock(fd, F_SETLKW, F_WRLCK) |
| 286 | #define Unlock(fd) \ |
| 287 | start_lock(fd, F_SETLK, F_UNLCK) |
| 288 | #define Is_readlock(fd) \ |
| 289 | test_lock(fd, F_RDLCK) |
| 290 | #define Is_writelock(fd) \ |
| 291 | test_lock(fd, F_WRLCK) |
| 292 | |
| 293 | /* |
| 294 | * communication between this process and weblog - sends user credentials |
| 295 | * over a shared pipe (mutex provided using locks) and recieves either a |
| 296 | * token or an error message |
| 297 | */ |
| 298 | static int |
| 299 | request_Authentication(char *user, char *passwd, char *cell, char *type, |
| 300 | char *tokenbuf, char *reason) |
| 301 | { |
| 302 | int len = 0; |
| 303 | int pid; |
| 304 | char *temp; |
| 305 | int lockfd = 0; |
| 306 | |
| 307 | afsassert(user); |
| 308 | afsassert(passwd); |
| 309 | afsassert(cell); |
| 310 | afsassert(type); |
| 311 | afsassert(tokenbuf); |
| 312 | |
| 313 | /* |
| 314 | * lock the pipe before beginning communication or in case of AIX it is an |
| 315 | * error to attempt to lock a pipe or FIFO (EINVAL) therefore we have to create |
| 316 | * a temporary file and use that fd instead |
| 317 | */ |
| 318 | #ifdef AIX |
| 319 | lockfd = tempfd; |
| 320 | #else |
| 321 | lockfd = writePipe; |
| 322 | #endif |
| 323 | |
| 324 | while ((pid = Is_writelock(lockfd)) != 0) { |
| 325 | if (pid < 0) { |
| 326 | afslog(5, |
| 327 | ("%s: pid:%d Error locking pipe - %s", module_name, |
| 328 | getpid(), strerror(errno))); |
| 329 | return -1; |
| 330 | } |
| 331 | afslog(40, |
| 332 | ("%s: pid %d waiting for lock held by pid %d", module_name, |
| 333 | getpid(), pid)); |
| 334 | } |
| 335 | |
| 336 | if (Write_lock(lockfd) == -1) { |
| 337 | afslog(5, |
| 338 | ("%s: pid:%d Error write lock - %s. Retrying with WriteW", |
| 339 | module_name, getpid(), strerror(errno))); |
| 340 | if (Writew_lock(lockfd) == -1) { |
| 341 | afslog(5, |
| 342 | ("%s: pid:%d Error write lock - %s", module_name, getpid(), |
| 343 | strerror(errno))); |
| 344 | return -1; |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | if (sendTo_afsAuthenticator(user, passwd, cell, type) == -1) { |
| 349 | Unlock(lockfd); |
| 350 | afslog(5, ("%s: Error sending authentication info", module_name)); |
| 351 | return -1; |
| 352 | } |
| 353 | |
| 354 | len = recvFrom_afsAuthenticator(tokenbuf); |
| 355 | |
| 356 | /* release the lock */ |
| 357 | if (Unlock(lockfd)) { |
| 358 | afslog(5, ("%s: pid:%d Error unlocking", module_name, getpid())); |
| 359 | return -1; |
| 360 | } |
| 361 | |
| 362 | if (len > 0) { |
| 363 | if (strncmp(tokenbuf, "FAILURE", 7) == 0) { |
| 364 | temp = &tokenbuf[7]; |
| 365 | strncpy(reason, temp, len); |
| 366 | return -2; |
| 367 | } |
| 368 | } |
| 369 | return len; |
| 370 | } |
| 371 | |
| 372 | /* |
| 373 | * pioctl setting token |
| 374 | */ |
| 375 | static int |
| 376 | setToken(char *tokenBuf, int tokenLen) |
| 377 | { |
| 378 | char *temp; |
| 379 | afs_int32 i = 0; |
| 380 | |
| 381 | afsassert(tokenBuf); |
| 382 | |
| 383 | /* |
| 384 | * set the primary flag only if we haven't done a SETPAG previoulsy |
| 385 | * by flipping this bit |
| 386 | */ |
| 387 | if (!doneSETPAG) { |
| 388 | #ifdef OLDSETPAG |
| 389 | /* skip over the secret token */ |
| 390 | temp = tokenBuf; |
| 391 | memcpy(&i, temp, sizeof(afs_int32)); |
| 392 | temp += (i + sizeof(afs_int32)); |
| 393 | |
| 394 | /* skip over the clear token */ |
| 395 | memcpy(&i, temp, sizeof(afs_int32)); |
| 396 | temp += (i + sizeof(afs_int32)); |
| 397 | |
| 398 | doneSETPAG = 1; |
| 399 | memcpy(&i, temp, sizeof(afs_int32)); |
| 400 | i |= 0x8000; |
| 401 | memcpy(temp, &i, sizeof(afs_int32)); |
| 402 | temp += sizeof(afs_int32); |
| 403 | #endif |
| 404 | |
| 405 | if (do_setpag()) { |
| 406 | return -1; |
| 407 | } |
| 408 | doneSETPAG = 1; |
| 409 | } |
| 410 | return do_pioctl(tokenBuf, tokenLen, tokenBuf, tokenLen, VIOCSETTOK, NULL, |
| 411 | 0); |
| 412 | } |
| 413 | |
| 414 | /* |
| 415 | * Get the token for the primary cell from the cache manager for this |
| 416 | * process. Primary cell is the cell at the first index (index 0) |
| 417 | */ |
| 418 | static int |
| 419 | getToken(char *buf, int bufsize) |
| 420 | { |
| 421 | /* get just the ONE token for this PAG from cache manager */ |
| 422 | afs_int32 i = 0; |
| 423 | memcpy((void *)buf, (void *)&i, sizeof(afs_int32)); |
| 424 | return do_pioctl(buf, sizeof(afs_int32), buf, bufsize, VIOCGETTOK, NULL, |
| 425 | 0); |
| 426 | } |
| 427 | |
| 428 | |
| 429 | /* |
| 430 | * discard all authentication information for this PAG ie. this process |
| 431 | */ |
| 432 | int |
| 433 | unlog() |
| 434 | { |
| 435 | return do_pioctl(0, 0, 0, 0, VIOCUNPAG, NULL, 0); |
| 436 | } |
| 437 | |
| 438 | |
| 439 | /* |
| 440 | * Does the following things: |
| 441 | * Checks whether there is a Basic Authentication header - obtains creds. |
| 442 | * Checks local cache for the token associated with the user creds. |
| 443 | * - if no token in cache - obtains token from weblog using pipes |
| 444 | * - sets the token and returns appropriate return code |
| 445 | * Return values: OK, SERVER_ERROR, AUTH_REQUIRED, FORBIDDEN |
| 446 | */ |
| 447 | int |
| 448 | authenticateUser(request_rec * r, char *defaultCell, int cacheExpiration, |
| 449 | char *type) |
| 450 | { |
| 451 | char user[APACHEAFS_USERNAME_MAX]; |
| 452 | char passwd[APACHEAFS_PASSWORD_MAX]; |
| 453 | char cell[APACHEAFS_CELLNAME_MAX]; |
| 454 | char tokenbuf[MAXBUFF]; |
| 455 | char cksum[SHA_HASH_BYTES]; |
| 456 | int rc = 0; |
| 457 | int i = 0; |
| 458 | const char *auth_line; |
| 459 | char reason[MAXBUFF]; /* if authentication failed - this is why */ |
| 460 | char err_msg[MAXBUFF]; |
| 461 | int userChanged = 0; |
| 462 | |
| 463 | afsassert(r); |
| 464 | afsassert(r->uri); |
| 465 | afsassert(defaultCell); |
| 466 | afsassert(type); |
| 467 | |
| 468 | auth_line = TABLE_GET(r->headers_in, "Authorization"); |
| 469 | |
| 470 | if (strcmp(global_default_cell, defaultCell)) { |
| 471 | strcpy(global_default_cell, defaultCell); |
| 472 | } |
| 473 | |
| 474 | memset(user, 0, APACHEAFS_USERNAME_MAX); |
| 475 | memset(passwd, 0, APACHEAFS_PASSWORD_MAX); |
| 476 | memset(cell, 0, APACHEAFS_CELLNAME_MAX); |
| 477 | |
| 478 | if (auth_line == NULL) { /* No Authorization field - we don't do anything */ |
| 479 | /* |
| 480 | * No Authorization field recieved - that's fine by us. |
| 481 | * go ahead and attempt to service the request and if we get |
| 482 | * back FORBIDDEN then we'll take care of it then |
| 483 | */ |
| 484 | afslog(15, ("%s: No authline recieved", module_name)); |
| 485 | haveAuth = 0; |
| 486 | userChanged = 1; |
| 487 | memset(lastUser, 0, APACHEAFS_USERNAME_MAX); |
| 488 | memset(lastCell, 0, APACHEAFS_CELLNAME_MAX); |
| 489 | memset(lastCksum, 0, SHA_HASH_BYTES); |
| 490 | rc = unlog(); |
| 491 | afslog(25, |
| 492 | ("%s: pid:%d No Authorization field. Unlogging ...", |
| 493 | module_name, getpid())); |
| 494 | if (rc) { |
| 495 | sprintf(err_msg, |
| 496 | "%s: Error unlogging from AFS cell - rc: %d, errno:%d", |
| 497 | module_name, rc, errno); |
| 498 | LOG_REASON(err_msg, r->uri, r); |
| 499 | return SERVER_ERROR; |
| 500 | } |
| 501 | return OK; |
| 502 | } |
| 503 | |
| 504 | /* |
| 505 | * We should get here only if there IS an Authorization field |
| 506 | */ |
| 507 | |
| 508 | if ((rc = parse_authhdr(r, user, passwd, cell, defaultCell)) != 0) { |
| 509 | sprintf(err_msg, "%s: Error parsing Authorization Header rc:%d", |
| 510 | module_name, rc); |
| 511 | LOG_REASON(err_msg, r->uri, r); |
| 512 | return rc; /* SERVER ERROR */ |
| 513 | } |
| 514 | |
| 515 | /* |
| 516 | * should get here only after obtaining the username and password and cell |
| 517 | * check to make sure anyway |
| 518 | */ |
| 519 | if ((user[0] == '\0') || (cell[0] == '\0') || (passwd[0] == '\0')) { |
| 520 | afslog(15, |
| 521 | ("%s: pid:%d No username or password or cell. Unlogging.", |
| 522 | module_name, getpid())); |
| 523 | haveAuth = 0; |
| 524 | userChanged = 1; |
| 525 | memset(lastUser, 0, APACHEAFS_USERNAME_MAX); |
| 526 | memset(lastCell, 0, APACHEAFS_CELLNAME_MAX); |
| 527 | memset(lastCksum, 0, SHA_HASH_BYTES); |
| 528 | rc = unlog(); |
| 529 | if (rc) { |
| 530 | sprintf(err_msg, |
| 531 | "%s: Error unlogging from AFS cell - rc: %d, errno:%d", |
| 532 | module_name, rc, errno); |
| 533 | LOG_REASON(err_msg, r->uri, r); |
| 534 | return SERVER_ERROR; |
| 535 | } |
| 536 | setCellAuthHeader(r); |
| 537 | return AUTH_REQUIRED; |
| 538 | } |
| 539 | #ifdef DEBUG |
| 540 | fprintf(stderr, "Cell:%s\tUser:%s\tPasswd:%s\n", cell, user, passwd); |
| 541 | #endif |
| 542 | |
| 543 | /* |
| 544 | * compare with previous username/cell/cksum - update it |
| 545 | * unlog if required |
| 546 | */ |
| 547 | |
| 548 | weblog_login_checksum(user, cell, passwd, cksum); |
| 549 | if (!haveAuth) { |
| 550 | haveAuth = 1; |
| 551 | strcpy(lastUser, user); |
| 552 | strcpy(lastCksum, cksum); |
| 553 | strcpy(lastCell, cell); |
| 554 | } |
| 555 | if (strcmp(user, lastUser) || strcmp(cell, lastCell) |
| 556 | || strcmp(cksum, lastCksum)) { |
| 557 | /* |
| 558 | * unlog the old user from the cell if a new username/passwd is recievd |
| 559 | */ |
| 560 | |
| 561 | userChanged = 1; |
| 562 | afslog(25, |
| 563 | ("%s: pid:%d\tUnlogging user %s from cell%s", module_name, |
| 564 | getpid(), lastUser, lastCell)); |
| 565 | afslog(25, ("%s:New user:%s\t New Cell:%s", module_name, user, cell)); |
| 566 | afslog(25, ("%s:Trying to get URL:%s", module_name, r->uri)); |
| 567 | afslog(25, ("%s: Unlogging ....", module_name)); |
| 568 | |
| 569 | if (unlog()) { |
| 570 | sprintf(err_msg, |
| 571 | "%s: Error unlogging from AFS cell - rc: %d, errno:%d", |
| 572 | module_name, rc, errno); |
| 573 | LOG_REASON(err_msg, r->uri, r); |
| 574 | return SERVER_ERROR; |
| 575 | } |
| 576 | /* change lastUser to this user */ |
| 577 | strcpy(lastUser, user); |
| 578 | strcpy(lastCksum, cksum); |
| 579 | strcpy(lastCell, cell); |
| 580 | } |
| 581 | |
| 582 | /* strcmp checksums - ie. did the user change */ |
| 583 | #ifndef NO_AFSAPACHE_CACHE |
| 584 | if (!cache_initialized) { |
| 585 | token_cache_init(); |
| 586 | cache_initialized = 1; |
| 587 | } |
| 588 | |
| 589 | /* have to check local cache for this name/passwd */ |
| 590 | |
| 591 | rc = check_Cache(user, passwd, cell, tokenbuf); |
| 592 | if (rc) { |
| 593 | /* if found then just send the request without going through |
| 594 | * weblog - this means the user has already been authenticated |
| 595 | * once and we have a valid token just need to set it - |
| 596 | * only if it is different from the token already set. No need to |
| 597 | * even unlog because this token is set for the entire PAG which |
| 598 | * of course consists of just this child process |
| 599 | */ |
| 600 | afslog(35, |
| 601 | ("%s: pid:%d found user %s's token (expires:%d) in cache", |
| 602 | module_name, getpid(), user, |
| 603 | (getExpiration(tokenbuf) - time(NULL)))); |
| 604 | |
| 605 | /* if the user changed then set this token else leave it since it should |
| 606 | * be set */ |
| 607 | if (userChanged) { |
| 608 | /* set this token obtained from the local cache */ |
| 609 | afslog(15, |
| 610 | ("%s:pid:%d\t Setting cached token", module_name, |
| 611 | getpid())); |
| 612 | if (setToken(tokenbuf, rc)) { |
| 613 | afslog(5, |
| 614 | ("%s: pid:%d Failed to set token obtained from cache." |
| 615 | "rc:%d errno:%d Token Expiration:%d", module_name, |
| 616 | getpid(), rc, errno, |
| 617 | (getExpiration(tokenbuf) - time(NULL)))); |
| 618 | #ifdef DEBUG_CACHE |
| 619 | parseToken(tokenbuf); |
| 620 | #endif |
| 621 | /* |
| 622 | * BUG WORKAROUND: sometimes we fail while setting token |
| 623 | * with errno ESRCH indicating the named cell |
| 624 | * in the last field of the token is not recognized - but that's |
| 625 | * not quite true according to parseToken()!! Possibly corrupted |
| 626 | * tokens from the cache? |
| 627 | * Anyway we just get a new token from weblog |
| 628 | */ |
| 629 | goto reqAuth; |
| 630 | } |
| 631 | } /* if userChanged */ |
| 632 | else { |
| 633 | /* if this is a child process getting the request for the first time |
| 634 | * then there's no way this guy's got a token for us in which case |
| 635 | * getToken should fail with EDOM and that means we should set the token |
| 636 | * first and maybe set a static variable saying we have set a token? |
| 637 | */ |
| 638 | char temp[MAXBUFF]; |
| 639 | if (getToken(temp, sizeof(temp))) { |
| 640 | if (errno == EDOM) { |
| 641 | /* try setting the cached token */ |
| 642 | if (setToken(tokenbuf, rc)) { |
| 643 | /* |
| 644 | * same bug workaround here. ie. go to weblog if setting |
| 645 | * the cached token fails. |
| 646 | */ |
| 647 | sprintf(err_msg, |
| 648 | "%s: pid:%d Failed to set cached token." |
| 649 | "errno:%d rc:%d", module_name, getpid(), |
| 650 | errno, rc); |
| 651 | LOG_REASON(err_msg, r->uri, r); |
| 652 | goto reqAuth; |
| 653 | } |
| 654 | } else { |
| 655 | /* and again for any getToken failure other than EDOM */ |
| 656 | sprintf(err_msg, |
| 657 | "%s: Failed to get token: errno:%d rc:%d", |
| 658 | module_name, errno, rc); |
| 659 | LOG_REASON(err_msg, r->uri, r); |
| 660 | goto reqAuth; |
| 661 | } |
| 662 | } /* so we already have a token set since the gettoken succeeded */ |
| 663 | } |
| 664 | |
| 665 | /* to set the REMOTE_USER environment variable */ |
| 666 | strcpy(r->connection->user, user); |
| 667 | return OK; |
| 668 | } |
| 669 | /* |
| 670 | * else - request afs_Authenticator's for it and update local cache |
| 671 | * then go about serving the request URI |
| 672 | */ |
| 673 | else { |
| 674 | reqAuth: |
| 675 | #endif /* NO_AFSAPACHE_CACHE */ |
| 676 | |
| 677 | rc = request_Authentication(user, passwd, cell, type, tokenbuf, |
| 678 | reason); |
| 679 | if (rc > 0) { |
| 680 | /* we got back a token from weblog */ |
| 681 | /* set the token with setToken */ |
| 682 | if (setToken(tokenbuf, rc)) { |
| 683 | sprintf(err_msg, |
| 684 | "%s: Failed to set token given by weblog. errno:%d", |
| 685 | module_name, errno); |
| 686 | LOG_REASON(err_msg, r->uri, r); |
| 687 | return SERVER_ERROR; |
| 688 | } |
| 689 | #ifdef DEBUG_TOKENS |
| 690 | system("/usr/afsws/bin/tokens"); |
| 691 | #endif |
| 692 | |
| 693 | #ifndef NO_AFSAPACHE_CACHE |
| 694 | /* update local cache */ |
| 695 | if (updateCache(user, passwd, cell, tokenbuf, cacheExpiration)) { |
| 696 | sprintf(err_msg, "%s: Error updating cache", module_name); |
| 697 | LOG_REASON(err_msg, r->uri, r); |
| 698 | return SERVER_ERROR; |
| 699 | } |
| 700 | afslog(15, |
| 701 | ("%s: pid:%d\t put user:%s tokens in cache", module_name, |
| 702 | getpid(), user)); |
| 703 | #endif /* NO_AFSAPACHE_CACHE */ |
| 704 | |
| 705 | /* now we've got a token, updated the cache and set it so we should |
| 706 | * have no problems accessing AFS files - however if we do then |
| 707 | * we handle it in afs_accessCheck() when the error comes back |
| 708 | */ |
| 709 | |
| 710 | /* to set the REMOTE_USER environment variable to the username */ |
| 711 | strcpy(r->connection->user, user); |
| 712 | return OK; |
| 713 | } else if (rc == -2) { |
| 714 | sprintf(err_msg, |
| 715 | ":%s: AFS authentication failed for %s@%s because %s", |
| 716 | module_name, user, cell, reason); |
| 717 | LOG_REASON(err_msg, r->uri, r); |
| 718 | setCellAuthHeader(r); |
| 719 | return AUTH_REQUIRED; |
| 720 | } else if (rc == -1) { |
| 721 | sprintf(err_msg, "%s: Error readiong from pipe. errno:%d", |
| 722 | module_name, errno); |
| 723 | LOG_REASON(err_msg, r->uri, r); |
| 724 | return SERVER_ERROR; |
| 725 | } |
| 726 | |
| 727 | else { |
| 728 | /* |
| 729 | * unknown error from weblog - this should not occur |
| 730 | * if afs_Authenticator can't authenticate you, then return FORBIDDEN |
| 731 | */ |
| 732 | sprintf(err_msg, |
| 733 | "%s: AFS could not authenticate user %s in cell %s." |
| 734 | "Returning FORBIDDEN", module_name, user, cell); |
| 735 | LOG_REASON(err_msg, r->uri, r); |
| 736 | return FORBIDDEN; |
| 737 | } |
| 738 | #ifndef NO_AFSAPACHE_CACHE |
| 739 | } |
| 740 | #endif |
| 741 | /* should never get here */ |
| 742 | LOG_REASON("AFS Authentication: WE SHOULD NEVER GET HERE", r->uri, r); |
| 743 | return SERVER_ERROR; |
| 744 | } |
| 745 | |
| 746 | |
| 747 | /* |
| 748 | * pioctl call to get the cell name hosting the object specified by path. |
| 749 | * returns 0 if successful -1 if failure. Assumes memory has been allocated |
| 750 | * for cell. Used to set the www-authenticate header. |
| 751 | */ |
| 752 | static int |
| 753 | get_cellname_from_path(char *apath, char *cell) |
| 754 | { |
| 755 | int rc; |
| 756 | |
| 757 | afsassert(apath); |
| 758 | afsassert(cell); |
| 759 | |
| 760 | rc = do_pioctl(NULL, 0, cell, APACHEAFS_CELLNAME_MAX, VIOC_FILE_CELL_NAME, |
| 761 | apath, 1); |
| 762 | if (rc) |
| 763 | afslog(30, |
| 764 | ("%s: Error getting cell from path %s. errno:%d rc:%d", |
| 765 | module_name, apath, errno, rc)); |
| 766 | else |
| 767 | afslog(30, |
| 768 | ("%s: Obtained cell %s from path %s", module_name, cell, |
| 769 | apath)); |
| 770 | |
| 771 | return rc; |
| 772 | } |
| 773 | |
| 774 | /* |
| 775 | * obtains the path to the file requested and sets things up to |
| 776 | * call get_cell_by_name. |
| 777 | * TODO: These could well be combined into one single function. |
| 778 | */ |
| 779 | static int |
| 780 | getcellname(request_rec * r, char *buf) |
| 781 | { |
| 782 | int rc = 0; |
| 783 | |
| 784 | afsassert(r); |
| 785 | afsassert(buf); |
| 786 | |
| 787 | if (r->filename) { |
| 788 | rc = get_cellname_from_path(r->filename, buf); |
| 789 | } else { |
| 790 | char path[1024]; |
| 791 | sprintf(path, "%s/%s", DOCUMENT_ROOT(r), r->uri); |
| 792 | rc = get_cellname_from_path(path, buf); |
| 793 | } |
| 794 | return rc; |
| 795 | } |
| 796 | |
| 797 | /* |
| 798 | * Returns a part of the url upto the second slash in the buf |
| 799 | */ |
| 800 | static int |
| 801 | geturi(request_rec * r, char *buf) |
| 802 | { |
| 803 | int rc = 0; |
| 804 | char *pos; |
| 805 | char *end; |
| 806 | int i = 0; |
| 807 | int max = 2; |
| 808 | |
| 809 | afsassert(r); |
| 810 | afsassert(buf); |
| 811 | |
| 812 | memset(buf, 0, APACHEAFS_CELLNAME_MAX); |
| 813 | pos = strchr(r->uri, '/'); |
| 814 | if (pos != NULL) { |
| 815 | pos++; |
| 816 | for (i = 0; i < max; i++) { |
| 817 | end = strchr(pos, '/'); |
| 818 | if (end != NULL) { |
| 819 | int len = strlen(pos) - strlen(end); |
| 820 | strcat(buf, "/"); |
| 821 | strncat(buf, pos, len); |
| 822 | afslog(35, |
| 823 | ("%s: Getting URI upto second slash buf:%s", |
| 824 | module_name, buf)); |
| 825 | pos += (len + 1); |
| 826 | end = strchr(pos, '/'); |
| 827 | if (end == NULL) { |
| 828 | break; |
| 829 | } |
| 830 | } else { |
| 831 | strcat(buf, pos); |
| 832 | break; |
| 833 | } |
| 834 | } |
| 835 | } else { |
| 836 | strcpy(buf, " "); |
| 837 | } |
| 838 | return rc; |
| 839 | } |
| 840 | |
| 841 | /* |
| 842 | * This function recursively parses buf and places the output in msg |
| 843 | * Eg. <%c%uUnknown> gets translated to the cellname that the file |
| 844 | * resides in, failing which the first part of the uri failing which the |
| 845 | * string Unknown |
| 846 | */ |
| 847 | static int |
| 848 | parseAuthName_int(request_rec * r, char *buf, char *msg) |
| 849 | { |
| 850 | char *pos; |
| 851 | char *end; |
| 852 | int len = 0; |
| 853 | int rc = 0; |
| 854 | char blank[APACHEAFS_CELLNAME_MAX]; |
| 855 | |
| 856 | afsassert(r); |
| 857 | afsassert(buf); |
| 858 | afsassert(msg); |
| 859 | |
| 860 | memset(blank, 0, sizeof(blank)); |
| 861 | afslog(50, |
| 862 | ("%s: Parsing Authorization Required reply. buf:%s", module_name, |
| 863 | buf)); |
| 864 | |
| 865 | pos = strchr(buf, '<'); |
| 866 | if (pos) { |
| 867 | len = strlen(pos); |
| 868 | pos++; |
| 869 | end = strchr(pos, '>'); |
| 870 | if (end == NULL) { |
| 871 | afslog(0, |
| 872 | ("%s:Parse error for AUTH_REQUIRED reply - mismatched <", |
| 873 | module_name)); |
| 874 | fprintf(stderr, "Parse Error: mismatched <\n"); |
| 875 | strncpy(msg, buf, strlen(buf) - len); |
| 876 | afslog(0, ("%s: msg:%s", msg)); |
| 877 | return -1; |
| 878 | } |
| 879 | end++; |
| 880 | if (pos[0] == '%') { |
| 881 | pos++; |
| 882 | switch (pos[0]) { |
| 883 | case 'c': |
| 884 | rc = getcellname(r, blank); |
| 885 | if (!rc) { |
| 886 | strncpy(msg, buf, strlen(buf) - len); |
| 887 | strcat(msg, blank); |
| 888 | strcat(msg, end); |
| 889 | return 0; |
| 890 | } |
| 891 | break; |
| 892 | |
| 893 | case 'u': |
| 894 | rc = geturi(r, blank); |
| 895 | if (!rc) { |
| 896 | strncpy(msg, buf, strlen(buf) - len); |
| 897 | strcat(msg, blank); |
| 898 | strcat(msg, end); |
| 899 | return 0; |
| 900 | } |
| 901 | break; |
| 902 | |
| 903 | case 'd': |
| 904 | if (global_default_cell != NULL) { |
| 905 | strncpy(msg, buf, strlen(buf) - len); |
| 906 | strcat(msg, global_default_cell); |
| 907 | strcat(msg, end); |
| 908 | return 0; |
| 909 | } |
| 910 | break; |
| 911 | } |
| 912 | strncpy(msg, buf, strlen(buf) - len); |
| 913 | strcat(msg, "<"); |
| 914 | pos++; |
| 915 | strcat(msg, pos); |
| 916 | strcpy(buf, msg); |
| 917 | memset(msg, 0, 1024); |
| 918 | parseAuthName_int(r, buf, msg); |
| 919 | return 0; |
| 920 | } else { |
| 921 | strncpy(msg, buf, strlen(buf) - len); |
| 922 | strncat(msg, pos, strlen(pos) - strlen(end) - 1); |
| 923 | strcat(msg, end); |
| 924 | return 0; |
| 925 | } |
| 926 | } |
| 927 | } |
| 928 | |
| 929 | /* |
| 930 | * Parses the entire auth_name string - ie. takes care of multiple |
| 931 | * <%...> <%...> |
| 932 | */ |
| 933 | static int |
| 934 | parseAuthName(request_rec * r, char *buf) |
| 935 | { |
| 936 | char *pos; |
| 937 | int rc; |
| 938 | char msg[1024]; |
| 939 | |
| 940 | afsassert(r); |
| 941 | afsassert(buf); |
| 942 | |
| 943 | memset(msg, 0, sizeof(msg)); |
| 944 | |
| 945 | pos = strchr(buf, '<'); |
| 946 | while (pos != NULL) { |
| 947 | rc = parseAuthName_int(r, buf, msg); |
| 948 | if (rc) { |
| 949 | strcpy(buf, msg); |
| 950 | afslog(35, |
| 951 | ("%s: Failed to parse Auth Name. buf:%s", module_name, |
| 952 | buf)); |
| 953 | return -1; |
| 954 | } |
| 955 | strcpy(buf, msg); |
| 956 | memset(msg, 0, sizeof(msg)); |
| 957 | pos = strchr(buf, '<'); |
| 958 | } |
| 959 | afslog(50, |
| 960 | ("%s: Parsing WWW Auth required reply. final message:%s", |
| 961 | module_name, buf)); |
| 962 | return 0; |
| 963 | } |
| 964 | |
| 965 | |
| 966 | /* |
| 967 | * Set the www-authenticate header - this is the login prompt the users see |
| 968 | */ |
| 969 | static int |
| 970 | setCellAuthHeader(request_rec * r) |
| 971 | { |
| 972 | char *name; |
| 973 | char buf[1024]; |
| 974 | int rc = 0; |
| 975 | |
| 976 | afsassert(r); |
| 977 | |
| 978 | name = (char *)get_afs_authprompt(r); |
| 979 | if (name != NULL) { |
| 980 | strcpy(buf, name); |
| 981 | rc = parseAuthName(r, buf); |
| 982 | } else { |
| 983 | strcpy(buf, " "); |
| 984 | } |
| 985 | TABLE_SET(r->err_headers_out, "WWW-Authenticate", |
| 986 | PSTRCAT(r->pool, "Basic realm=\"", buf, "\"", NULL)); |
| 987 | return rc; |
| 988 | } |
| 989 | |
| 990 | |
| 991 | /* |
| 992 | * Checks if we have some authentication credentials, if we do returns |
| 993 | * FORBIDDEN and if we don't then returns AUTH_REQUIRED with the appropriate |
| 994 | * www-authenticate header. Should be called if we can't access a file because |
| 995 | * permission is denied. |
| 996 | */ |
| 997 | int |
| 998 | forbToAuthReqd(request_rec * r) |
| 999 | { |
| 1000 | if (haveAuth) { |
| 1001 | return FORBIDDEN; |
| 1002 | } else { |
| 1003 | setCellAuthHeader(r); |
| 1004 | return AUTH_REQUIRED; |
| 1005 | } |
| 1006 | } |