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