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