1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /* 9 * RPC server procedures for the usermode daemon kwarnd. 10 */ 11 12 #include <stdio.h> 13 #include <unistd.h> 14 #include <pwd.h> 15 #include <grp.h> 16 #include <strings.h> 17 #include <string.h> 18 #include <sys/param.h> 19 #include <sys/syslog.h> 20 #include "kwarnd.h" 21 #include <rpc/rpc.h> 22 #include <stdlib.h> 23 #include <syslog.h> 24 #include <poll.h> 25 #include <utmpx.h> 26 #include <pwd.h> 27 #include <strings.h> 28 #include <ctype.h> 29 30 #include <k5-int.h> 31 #include <profile/prof_int.h> 32 #include <com_err.h> 33 #include <libintl.h> 34 #include <krb5.h> 35 36 struct k5_data 37 { 38 krb5_context ctx; 39 krb5_ccache cc; 40 krb5_principal me; 41 char *name; 42 }; 43 44 45 #define MAIL "mail" 46 #define MAILPATH "/usr/bin/mail" 47 #define DEFAULT_CONFIG "* terminal 30m" 48 #define CONF_FILENAME "/etc/krb5/warn.conf" 49 50 /* warn.conf info */ 51 52 typedef struct config_entry_s { 53 struct config_entry_s *next; 54 int seconds_to_warn; 55 char *principal; 56 char *where_to; 57 char *email; 58 int renew; 59 int log_success; 60 int log_failure; 61 } config_entry_list_t; 62 static config_entry_list_t *config_entry_list; 63 64 /* list of principals to be warned */ 65 66 typedef struct cred_warning_list_s { 67 struct cred_warning_list_s *next; 68 WARNING_NAME_T warn_name; 69 time_t cred_exp_time; 70 time_t cred_warn_time; 71 mutex_t cwm; 72 } cred_warning_list_t; 73 static cred_warning_list_t *cred_warning_list; 74 static rwlock_t cred_lock = DEFAULTRWLOCK; 75 76 static bool_t 77 del_warning_pvt(char *); 78 79 static config_entry_list_t * 80 find_warning_info(char *); 81 82 static bool_t 83 parseConfigLine(char *buffer); 84 85 extern 86 warn_send(char *, char *); 87 88 extern int kwarnd_debug; 89 90 cred_warning_list_t * 91 find_cred_warning(WARNING_NAME_T warn_name) 92 { 93 cred_warning_list_t *cw; 94 if (!cred_warning_list) 95 return (NULL); 96 for (cw = cred_warning_list; cw != NULL; cw = cw->next) { 97 if (strcmp(warn_name, cw->warn_name) != 0) 98 continue; 99 return (cw); 100 } 101 return (NULL); 102 } 103 104 /* 105 * add a principal to the principal warning list 106 */ 107 108 bool_t 109 kwarn_add_warning_1_svc(kwarn_add_warning_arg *argp, 110 kwarn_add_warning_res *res, 111 struct svc_req *rqstp) 112 { 113 cred_warning_list_t *cred_warning; 114 config_entry_list_t *config_entry; 115 116 if (kwarnd_debug) { 117 printf("kwarn_add_warning_1_svc start; cWlist=%p\n", 118 cred_warning_list); 119 120 printf("kwarn_add_warning_1_svc: principal %s", 121 argp->warning_name); 122 printf(" exp time: %d\n", argp->cred_exp_time); 123 } 124 125 /* 126 * if there is no entry in the config file that matches the principal to 127 * be added to the warning list, return true because we are not going to 128 * send a warning for this principal. 129 */ 130 131 if ((config_entry = find_warning_info(argp->warning_name)) == NULL) { 132 if (kwarnd_debug) 133 printf( 134 "kwarn_add_warning_1_svc find_warn_info: fails, cWlist=%p\n", 135 cred_warning_list); 136 137 return (TRUE); 138 } 139 140 /* 141 * see if a warning has already been created for this principal, if so 142 * update the warning time. 143 */ 144 145 rw_wrlock(&cred_lock); 146 if (cred_warning = find_cred_warning(argp->warning_name)) { 147 rw_unlock(&cred_lock); 148 mutex_lock(&cred_warning->cwm); 149 cred_warning->cred_exp_time = argp->cred_exp_time; 150 cred_warning->cred_warn_time = argp->cred_exp_time 151 - config_entry->seconds_to_warn; 152 mutex_unlock(&cred_warning->cwm); 153 } else { 154 cred_warning = (cred_warning_list_t *)malloc( 155 sizeof (*cred_warning_list)); 156 if (cred_warning == NULL) { 157 rw_unlock(&cred_lock); 158 res->status = 1; 159 return (FALSE); 160 } 161 (void) memset((char *)cred_warning, 0, 162 sizeof (*cred_warning_list)); 163 cred_warning->cred_exp_time = argp->cred_exp_time; 164 cred_warning->cred_warn_time = argp->cred_exp_time 165 - config_entry->seconds_to_warn; 166 cred_warning->warn_name = strdup(argp->warning_name); 167 if (cred_warning->warn_name == NULL) { 168 free(cred_warning); 169 rw_unlock(&cred_lock); 170 res->status = 1; 171 return (FALSE); 172 } 173 mutex_init(&cred_warning->cwm, USYNC_THREAD, NULL); 174 cred_warning->next = cred_warning_list; 175 cred_warning_list = cred_warning; 176 rw_unlock(&cred_lock); 177 } 178 res->status = 0; 179 180 if (kwarnd_debug) 181 printf( 182 "kwarn_add_warning_1_svc end: returns true; cWlist=%p\n", 183 cred_warning_list); 184 185 return (TRUE); 186 } 187 188 /* 189 * delete a warning request for a given principal 190 */ 191 192 bool_t 193 kwarn_del_warning_1_svc(kwarn_del_warning_arg *argp, 194 kwarn_del_warning_res *res, 195 struct svc_req *rqstp) 196 { 197 if (kwarnd_debug) 198 printf(gettext("delete principal %s requested\n"), 199 argp->warning_name); 200 201 if (del_warning_pvt(argp->warning_name) == TRUE) { 202 res->status = 0; 203 204 if (kwarnd_debug) 205 printf(gettext("delete principal %s completed\n"), 206 argp->warning_name); 207 208 return (TRUE); 209 } else { 210 res->status = 1; 211 212 if (kwarnd_debug) 213 printf(gettext("delete principal %s failed\n"), 214 argp->warning_name); 215 216 return (TRUE); 217 } 218 } 219 220 static bool_t 221 del_warning_pvt(char *warning_name) 222 { 223 cred_warning_list_t *cred_warning, *prev; 224 rw_wrlock(&cred_lock); 225 for (prev = NULL, cred_warning = cred_warning_list; 226 cred_warning != NULL; prev = cred_warning, 227 cred_warning = cred_warning->next) { 228 if (strcmp(cred_warning->warn_name, warning_name) == 0) { 229 if (!prev) 230 cred_warning_list = cred_warning->next; 231 else 232 prev->next = cred_warning->next; 233 234 free(cred_warning->warn_name); 235 free(cred_warning); 236 rw_unlock(&cred_lock); 237 return (TRUE); 238 } 239 } 240 rw_unlock(&cred_lock); 241 return (FALSE); 242 } 243 244 /* 245 * load the warn.conf file into the config_entry list. 246 */ 247 248 bool_t 249 loadConfigFile(void) 250 { 251 char buffer[BUFSIZ]; 252 FILE *cfgfile; 253 bool_t retval = TRUE; 254 255 if ((cfgfile = fopen(CONF_FILENAME, "r")) == NULL) { 256 syslog(LOG_ERR, gettext( 257 "could not open config file \"%s\"\n"), 258 CONF_FILENAME); 259 syslog(LOG_ERR, gettext( 260 "using default options \"%s\"\n"), 261 DEFAULT_CONFIG); 262 retval = parseConfigLine(DEFAULT_CONFIG); 263 } else { 264 (void) memset(buffer, 0, sizeof (buffer)); 265 while ((fgets(buffer, BUFSIZ, cfgfile) != NULL) && 266 (retval == TRUE)) 267 retval = parseConfigLine(buffer); 268 fclose(cfgfile); 269 } 270 return (retval); 271 } 272 273 /* 274 * Return TRUE if we get a valid opt and update flags appro. 275 */ 276 static bool_t 277 cmp_renew_opts(char *opt, 278 int *log_success, /* out */ 279 int *log_failure) /* out */ 280 { 281 282 if (strncasecmp(opt, "log", 283 sizeof ("log")) == 0) { 284 *log_success = *log_failure = 1; 285 } else if (strncasecmp(opt, "log-success", 286 sizeof ("log-success")) == 0) { 287 *log_success = 1; 288 } else if (strncasecmp(opt, "log-failure", 289 sizeof ("log-failure")) == 0) { 290 *log_failure = 1; 291 } else { 292 if (kwarnd_debug) 293 printf("cmp_renew_opts: renew bad opt=`%s'\n", 294 opt ? opt : "null"); 295 return (FALSE); 296 } 297 298 return (TRUE); 299 } 300 301 /* 302 * Make the config_entry item for the config_entry_list, based on 303 * buffer. The formats are 304 * 305 * <principal> [renew[:<opt1,...optN>]] syslog|terminal <time> 306 * <principal> [renew[:<opt1,...optN>]] mail <time> <e-mail address> 307 * 308 * where renew opts will be: 309 * 310 * log-success 311 * - Log the result of the renew attempt on success using 312 * the specified method (syslog|terminal|mail) 313 * 314 * log-failure 315 * - Log the result of the renew attempt on failure using 316 * the specified method (syslog|terminal|mail) 317 * 318 * log 319 * - Same as specifing both log-failure and log-success 320 * 321 * Note if no log options are given, there will be no logging. 322 * 323 */ 324 325 static bool_t 326 parseConfigLine(char *buffer) 327 { 328 char *principal, *send_to, *emailid, *ends, *tm; 329 char *exptime; 330 int time_mode; 331 time_t etime; 332 config_entry_list_t *config_entry; 333 int renew = 0; 334 int log_success = 0; 335 int log_failure = 0; 336 337 /* ignore comments */ 338 if (*buffer == '#') 339 return (TRUE); 340 341 if (kwarnd_debug) 342 printf("parseconf: buffer=%s", buffer); 343 344 /* find end of principal */ 345 principal = buffer; 346 for (send_to = buffer; *send_to && !isspace(*send_to); 347 send_to++); 348 349 /* find first non whitespace after principal (start of send_to) */ 350 if (*send_to) { 351 *send_to = '\0'; 352 send_to++; 353 while (*send_to && isspace(*send_to)) 354 send_to++; 355 } 356 357 /* if no send_to, continue, bad entry */ 358 if (! *send_to) 359 return (TRUE); 360 361 /* find end of send_to */ 362 for (ends = send_to; *ends && !isspace(*ends); 363 ends++); 364 if (*ends) 365 *ends = '\0'; 366 367 368 if (strchr(send_to, ':')) { 369 /* we've got renew opts */ 370 char *st = NULL, *op = NULL; 371 372 op = strdup(send_to); 373 if (!op) 374 return (FALSE); 375 st = strchr(op, ':'); 376 *st = '\0'; 377 378 if (strncasecmp(op, "renew", sizeof ("renew")) == 0) { 379 renew = 1; 380 } else { 381 free(op); 382 /* got a ':' but not preceeded w/renew, badent, skip */ 383 if (kwarnd_debug) 384 printf("parseconf: colon badent, skip\n"); 385 return (TRUE); 386 } 387 free(op); 388 op = NULL; 389 390 st++; 391 if (!st || !*st || isspace(*st)) { 392 if (kwarnd_debug) 393 printf("parseconf: st badent, skip\n"); 394 /* bad ent, skip */ 395 return (TRUE); 396 } 397 if (renew && strchr(st, ',')) { 398 while (1) { 399 /* loop thru comma seperated list-o-opts */ 400 char *comma = NULL, *c = NULL, *l = NULL; 401 402 if (st && (comma = strchr(st, ','))) { 403 l = strdup(st); 404 if (!l) 405 return (FALSE); 406 c = strchr(l, ','); 407 *c = '\0'; 408 if (!cmp_renew_opts(l, &log_success, 409 &log_failure)) { 410 free(l); 411 /* badent, skip */ 412 return (TRUE); 413 } 414 free(l); 415 l = NULL; 416 417 st = comma; 418 st++; 419 } else { 420 if (st) { 421 if (!cmp_renew_opts(st, 422 &log_success, 423 &log_failure)) { 424 /* badent, skip */ 425 return (TRUE); 426 } 427 } 428 break; 429 } 430 } /* while */ 431 } else if (st) { 432 /* we just have one opt */ 433 if (!cmp_renew_opts(st, &log_success, &log_failure)) { 434 /* badent, skip */ 435 return (TRUE); 436 } 437 } 438 439 /* if send_to is "renew", note it and refind send_to */ 440 } else if (strncasecmp(send_to, "renew", 441 sizeof ("renew")) == 0) { 442 renew = 1; 443 444 } 445 446 if (kwarnd_debug) { 447 printf("parseconf: renew=%d, log failure=%d, log success=%d\n", 448 renew, log_failure, log_success); 449 } 450 451 if (renew) { 452 /* find first non whitespace after send_to (start of exptime) */ 453 for (send_to = ends+1; *send_to && isspace(*send_to); 454 send_to++); 455 456 /* if no send_to, continue, bad entry */ 457 if (! *send_to) { 458 if (kwarnd_debug) 459 printf("parseconf: no send_to, badent, skip\n"); 460 return (TRUE); 461 } 462 463 /* find end of send_to */ 464 for (ends = send_to; *ends && !isspace(*ends); 465 ends++); 466 if (*ends) 467 *ends = '\0'; 468 } 469 470 471 /* find first non whitespace after send_to (start of exptime) */ 472 for (exptime = ends+1; *exptime && isspace(*exptime); 473 exptime++); 474 475 /* if no exptime, continue, bad entry */ 476 if (! *exptime) { 477 if (kwarnd_debug) 478 printf("parseconf: no exptime, badent, skip\n"); 479 return (TRUE); 480 } 481 482 /* find end of exptime */ 483 for (ends = exptime; *ends && !isspace(*ends); ends++); 484 485 tm = ends - 1; 486 if (*tm == 's') 487 time_mode = 1; 488 else if (*tm == 'm') 489 time_mode = 2; 490 else if (*tm == 'h') 491 time_mode = 3; 492 else 493 time_mode = 1; 494 495 if (*tm) 496 *tm = '\0'; 497 498 if (kwarnd_debug) { 499 printf("parseconf: send_to = '%s', exptime='%s'\n", 500 send_to, exptime); 501 } 502 503 /* find first non whitespace after exptime (start of emailid) */ 504 for (emailid = ends+1; *emailid && isspace(*emailid); emailid++); 505 506 /* find end of emailid */ 507 if (*emailid) { 508 for (ends = emailid; *ends && !isspace(*ends); 509 ends++); 510 511 if (*ends) 512 *ends = '\0'; 513 } 514 515 /* if send to mail and no mail address, bad entry */ 516 if ((strcmp(send_to, "mail") == 0) && (!*emailid)) { 517 if (kwarnd_debug) 518 printf("parseconf: returns true; no mail addr\n"); 519 520 syslog(LOG_ERR, gettext("missing mail address" 521 " in config entry: \n%s %s %s " 522 " cannot mail warning"), principal, 523 send_to, exptime); 524 return (TRUE); 525 } 526 527 /* create an entry */ 528 config_entry = (config_entry_list_t *) 529 malloc(sizeof (*config_entry_list)); 530 if (config_entry == NULL) 531 return (FALSE); 532 (void) memset(config_entry, 0, sizeof (*config_entry_list)); 533 config_entry->principal = strdup(principal); 534 if (config_entry->principal == NULL) 535 return (FALSE); 536 config_entry->where_to = strdup(send_to); 537 if (config_entry->where_to == NULL) 538 return (FALSE); 539 etime = atol(exptime); 540 if (time_mode == 1) 541 config_entry->seconds_to_warn = etime; 542 else if (time_mode == 2) 543 config_entry->seconds_to_warn = etime * 60; 544 else if (time_mode == 3) 545 config_entry->seconds_to_warn = etime * 60 * 60; 546 547 if (*emailid) { 548 config_entry->email = strdup(emailid); 549 if (config_entry->email == NULL) 550 return (FALSE); 551 } 552 553 config_entry->renew = renew; 554 config_entry->log_success = log_success; 555 config_entry->log_failure = log_failure; 556 config_entry->next = config_entry_list; 557 config_entry_list = config_entry; 558 if (kwarnd_debug) 559 printf("parseconf: returns true; celist=%p\n", 560 config_entry_list); 561 562 return (TRUE); 563 } 564 565 /* 566 * find a specific warn.conf entry. 567 */ 568 569 static config_entry_list_t * 570 find_warning_info(char *principal) 571 { 572 config_entry_list_t *config_entry; 573 /* look for a specific entry */ 574 for (config_entry = config_entry_list; config_entry; 575 config_entry = config_entry->next) { 576 if (strcmp(config_entry->principal, principal) == 0) { 577 return (config_entry); 578 } 579 } 580 /* look for a wild card entry */ 581 for (config_entry = config_entry_list; config_entry; 582 config_entry = config_entry->next) { 583 if (strcmp(config_entry->principal, "*") == 0) { 584 return (config_entry); 585 } 586 } 587 /* nothing found */ 588 return (NULL); 589 590 } 591 592 /* 593 * create a pipe, fork and exec a command, 594 */ 595 static FILE * 596 safe_popen_w(char *path_to_cmd, char **argv) 597 { 598 599 int fd[2]; 600 FILE *fp; 601 char *envp[2]; 602 603 if (pipe(fd) == -1) 604 return (NULL); 605 606 607 switch (fork()) { 608 case -1: 609 (void) close(fd[0]); 610 (void) close(fd[1]); 611 return (NULL); 612 613 case 0: 614 close(fd[1]); 615 /* fd[0] is the end we read from */ 616 if (fd[0] != 0) { 617 close(0); 618 dup(fd[0]); 619 } 620 close(1); 621 close(2); 622 envp[0] = "PATH=/usr/bin"; 623 envp[1] = NULL; 624 #ifdef DEBUG 625 { 626 int fd; 627 fd = open("/tmp/kwarn.out", O_WRONLY|O_TRUNC|O_CREAT, 628 0666); 629 if (fd != 1) 630 dup(fd); 631 if (fd != 2) 632 dup(fd); 633 } 634 #endif 635 (void) execve(path_to_cmd, argv, envp); 636 syslog(LOG_ERR, "warnd: %m"); 637 _exit(1); 638 639 default: 640 close(fd[0]); 641 /* fd[1] is the end we write to */ 642 643 fp = fdopen(fd[1], "w"); 644 645 if (fp == NULL) { 646 (void) close(fd[1]); 647 return (NULL); 648 } 649 return (fp); 650 } 651 } 652 653 654 static uid_t gssd_uid; 655 656 void 657 set_warnd_uid(uid_t uid) 658 { 659 660 /* 661 * set the value of gssd_uid, so it can be retrieved when getuid() 662 * is called by the underlying mechanism libraries 663 */ 664 if (kwarnd_debug) 665 printf("set_warnd_uid called with uid = %d\n", uid); 666 667 gssd_uid = uid; 668 } 669 670 uid_t 671 getuid(void) 672 673 { 674 675 /* 676 * return the value set when one of the gssd procedures was 677 * entered. This is the value of the uid under which the 678 * underlying mechanism library must operate in order to 679 * get the user's credentials. This call is necessary since 680 * gssd runs as root and credentials are many times stored 681 * in files and directories specific to the user 682 */ 683 if (kwarnd_debug) 684 printf("getuid called and returning gsssd_uid = %d\n", 685 gssd_uid); 686 687 return (gssd_uid); 688 } 689 690 691 static bool_t 692 getpruid(char *pr, uid_t *uid) 693 { 694 char *rcp1 = NULL, *rcp2 = NULL, *rcp3 = NULL; 695 struct passwd *pw; 696 697 rcp1 = strdup(pr); 698 if (!rcp1) 699 return (FALSE); 700 rcp2 = strtok(rcp1, "@"); 701 rcp3 = strtok(rcp2, "/"); 702 703 if (rcp3) { 704 pw = getpwnam(rcp3); 705 *uid = pw->pw_uid; 706 free(rcp1); 707 return (TRUE); 708 } 709 710 free(rcp1); 711 return (FALSE); 712 } 713 714 715 static krb5_error_code 716 renew_creds( 717 char *princ, 718 time_t *new_exp_time) /* out */ 719 { 720 krb5_creds my_creds; 721 krb5_error_code code = 0; 722 struct k5_data k5; 723 char *progname = "warnd"; 724 725 uid_t saved_u = getuid(); 726 uid_t u; 727 728 if (kwarnd_debug) 729 printf("renew start: uid=%d\n", getuid()); 730 731 if (!getpruid(princ, &u)) { 732 if (kwarnd_debug) 733 printf("renew: getpruid failed, princ='%s'\n", 734 princ ? princ : "<null>"); 735 736 return (-1); /* better err num? */ 737 } 738 739 set_warnd_uid(u); 740 741 (void) memset(&my_creds, 0, sizeof (my_creds)); 742 (void) memset(&k5, 0, sizeof (k5)); 743 744 if (code = krb5_init_context(&k5.ctx)) { 745 com_err(progname, code, 746 gettext("while initializing Kerberos 5 library")); 747 goto out; 748 } 749 750 if ((code = krb5_cc_default(k5.ctx, &k5.cc))) { 751 com_err(progname, code, 752 gettext("while getting default ccache")); 753 goto out; 754 755 } 756 757 if ((code = krb5_parse_name(k5.ctx, princ, 758 &k5.me))) { 759 com_err(progname, code, gettext("when parsing name %s"), 760 princ); 761 goto out; 762 } 763 764 if ((code = krb5_get_renewed_creds(k5.ctx, &my_creds, k5.me, k5.cc, 765 NULL))) { 766 com_err(progname, code, gettext("while renewing creds")); 767 goto out; 768 } 769 770 if (code = krb5_cc_initialize(k5.ctx, k5.cc, k5.me)) { 771 com_err(progname, code, gettext("when initializing cache %s"), 772 "defcc"); 773 goto out; 774 } 775 776 if (code = krb5_cc_store_cred(k5.ctx, k5.cc, &my_creds)) { 777 com_err(progname, code, gettext("while storing credentials")); 778 goto out; 779 } 780 781 /* "return" new expire time */ 782 *new_exp_time = my_creds.times.endtime; 783 784 out: 785 krb5_free_cred_contents(k5.ctx, &my_creds); 786 787 if (k5.name) 788 krb5_free_unparsed_name(k5.ctx, k5.name); 789 if (k5.me) 790 krb5_free_principal(k5.ctx, k5.me); 791 if (k5.cc) 792 krb5_cc_close(k5.ctx, k5.cc); 793 if (k5.ctx) 794 krb5_free_context(k5.ctx); 795 796 set_warnd_uid(saved_u); 797 798 if (kwarnd_debug) 799 printf("renew end: code=%s, uid=%d\n", error_message(code), 800 getuid()); 801 802 return (code); 803 } 804 805 static bool_t 806 loggedon(char *name) 807 { 808 register struct utmpx *ubuf; 809 char *rcp1 = NULL, *rcp2 = NULL, *rcp3 = NULL; 810 811 /* 812 * strip any realm or instance from principal so we can match 813 * against unix userid. 814 */ 815 rcp1 = strdup(name); 816 if (!rcp1) 817 return (FALSE); 818 rcp2 = strtok(rcp1, "@"); 819 rcp3 = strtok(rcp2, "/"); 820 821 /* 822 * Scan through the "utmpx" file for the 823 * entry for the person we want to send to. 824 */ 825 826 setutxent(); 827 while ((ubuf = getutxent()) != NULL) { 828 if (ubuf->ut_type == USER_PROCESS) { 829 if (strncmp(rcp3, ubuf->ut_user, 830 sizeof (ubuf->ut_user)) == 0) { 831 free(rcp1); 832 endutxent(); 833 return (TRUE); 834 835 } 836 } 837 } 838 free(rcp1); 839 endutxent(); 840 841 if (kwarnd_debug) 842 printf("loggedon: returning false for user `%s'\n", rcp1); 843 844 return (FALSE); 845 } 846 847 /* 848 * main loop to check the cred warning list and send the warnings 849 * the appropriate location based on warn.conf or auto-renew creds. 850 */ 851 852 void 853 kwarnd_check_warning_list(void) 854 { /* func */ 855 cred_warning_list_t *cw; /* cred warning */ 856 config_entry_list_t *ce; /* config entry */ 857 time_t now; 858 int minutes; 859 char buff[256]; 860 char cmdline[256]; 861 FILE *fp; 862 char *subj = "Kerberos credentials expiring"; 863 char *renew_subj = "Kerberos credentials renewed"; 864 865 if (kwarnd_debug) 866 printf("check list: start: getuid=%d, cw list=%p\n", getuid(), 867 cred_warning_list); 868 869 while (1) { 870 (void) poll(NULL, NULL, 60000); 871 872 for (cw = cred_warning_list; 873 cw != NULL; 874 cw = cw->next) { 875 int send_msg = 0; 876 877 time(&now); 878 if (now >= cw->cred_warn_time) { 879 int renew_attempted = 0; 880 int renew_failed = 0; 881 int renew_tooclose = 0; 882 883 if (kwarnd_debug) 884 printf("checklist: now >= warn_t\n"); 885 886 ce = find_warning_info(cw->warn_name); 887 minutes = (cw->cred_exp_time - 888 now + 59) / 60; 889 890 if (kwarnd_debug) 891 printf("checklist: where_to=%s\n", 892 ce->where_to ? 893 ce->where_to : "null"); 894 895 if (ce->renew && 896 loggedon(cw->warn_name)) { 897 krb5_error_code code; 898 time_t new_exp_time; 899 900 renew_attempted = 1; 901 code = renew_creds( 902 cw->warn_name, 903 &new_exp_time); 904 if (!code) { 905 /* krb5 api renew success */ 906 907 /* 908 * So we had api success 909 * but the new exp time 910 * is same as current one 911 * so we are too close 912 * to Renewable_life time. 913 */ 914 if (cw->cred_exp_time 915 == new_exp_time) { 916 renew_tooclose = 1; 917 if (kwarnd_debug) 918 printf( 919 "checklist: new expire time same as old expire time\n"); 920 921 if (ce->log_failure) { 922 send_msg = 1; 923 snprintf(buff, 924 sizeof (buff), 925 gettext("%s:\r\nYour kerberos" 926 " credentials have not been renewed" 927 " (too close to Renewable_life).\r\n" 928 "Please run kinit(1).\r\n"), 929 cw->warn_name); 930 } 931 } else { 932 /* update times */ 933 cw->cred_exp_time = 934 new_exp_time; 935 cw->cred_warn_time = 936 new_exp_time - 937 ce->seconds_to_warn; 938 } 939 940 if (kwarnd_debug) 941 printf( 942 "check list: new_w_t=%d\n", 943 cw->cred_warn_time); 944 945 if (!renew_tooclose && 946 ce->log_success) { 947 if (kwarnd_debug) 948 printf( 949 "check list: log success\n"); 950 951 send_msg = 1; 952 snprintf(buff, 953 sizeof (buff), 954 gettext("%s:\r\nYour kerberos" 955 " credentials have been renewed.\r\n"), 956 cw->warn_name); 957 } 958 959 } /* !(code) */ 960 961 if (!renew_tooclose && code && 962 ce->log_failure) { 963 if (kwarnd_debug) 964 printf( 965 "check list: log FAIL\n"); 966 967 send_msg = 1; 968 snprintf(buff, 969 sizeof (buff), 970 gettext("%s:\r\nYour kerberos" 971 " credentials failed to be renewed (%s).\r\n"), 972 cw->warn_name, 973 error_message(code)); 974 } 975 renew_failed = code ? 1 : 0; 976 977 } else if (minutes > 0) { 978 send_msg = 1; 979 snprintf(buff, sizeof (buff), 980 gettext("%s:\r\nyour kerberos" 981 " credentials expire in less than" 982 " %d minutes.\r\n"), 983 cw->warn_name, 984 minutes); 985 } else { 986 send_msg = 1; 987 snprintf(buff, sizeof (buff), 988 gettext("%s:\r\nyour kerberos" 989 " credentials have expired.\r\n"), 990 cw->warn_name); 991 } 992 993 if (kwarnd_debug) 994 printf("checklist: send_msg=%d\n", 995 send_msg); 996 if (!send_msg) 997 goto del_warning; 998 999 if (strncmp(ce->where_to, 1000 "mail", sizeof ("mail")) == 0) { 1001 char *argv[3]; 1002 1003 argv[0] = MAIL; 1004 (void) snprintf(cmdline, 1005 sizeof (cmdline), 1006 "%s", 1007 ce->email); 1008 argv[1] = cmdline; 1009 argv[2] = NULL; 1010 1011 fp = safe_popen_w(MAILPATH, argv); 1012 1013 if (fp) { 1014 1015 (void) fprintf(fp, 1016 "To: %s\nSubject: %s\n\n%s\n", 1017 ce->email, 1018 renew_attempted 1019 ? renew_subj : subj, 1020 buff); 1021 1022 fclose(fp); 1023 } else { 1024 syslog(LOG_ERR, 1025 gettext("could not fork " 1026 "mail program to e-mail " 1027 "warning to %s\n"), 1028 cmdline); 1029 } 1030 1031 } else if (strncmp(ce->where_to, 1032 "terminal", 1033 sizeof ("terminal")) == 0) { 1034 1035 warn_send(cw->warn_name, 1036 buff); 1037 1038 } else if (send_msg && strncmp(ce->where_to, 1039 "syslog", 1040 sizeof ("syslog")) == 0) { 1041 syslog(LOG_NOTICE|LOG_AUTH, 1042 "%s", 1043 buff); 1044 #if 0 1045 } else if (strncmp(ce->where_to, 1046 "snmp", 1047 sizeof ("snmp")) == 0) { 1048 #endif 1049 } else { 1050 if (kwarnd_debug) 1051 printf( 1052 "unknown msg method=`%s'\n", 1053 ce->where_to); 1054 1055 exit(1); 1056 } 1057 1058 del_warning: 1059 if (!renew_attempted || renew_failed || 1060 renew_tooclose) { 1061 if (del_warning_pvt(cw->warn_name) 1062 == TRUE) { 1063 1064 if (kwarnd_debug) 1065 printf( 1066 "check list: del warn succ\n"); 1067 1068 break; 1069 } else { 1070 if (kwarnd_debug) 1071 printf( 1072 "could not delete warning\n"); 1073 1074 syslog(LOG_ERR, gettext( 1075 "could not delete warning")); 1076 1077 exit(1); 1078 } 1079 } 1080 1081 } /* if (now) */ 1082 } /* for */ 1083 } /* while */ 1084 } /* func */ 1085