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 2007 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 krb5_cc_uid; 676 677 void 678 set_warnd_uid(uid_t uid) 679 { 680 /* 681 * set the value of krb5_cc_uid, so it can be retrieved when 682 * app_krb5_user_uid() is called by the underlying mechanism libraries. 683 */ 684 if (kwarnd_debug) 685 printf("set_warnd_uid called with uid = %d\n", uid); 686 krb5_cc_uid = uid; 687 } 688 689 uid_t 690 app_krb5_user_uid(void) 691 { 692 693 /* 694 * return the value set when one of the kwarnd procedures was 695 * entered. This is the value of the uid under which the 696 * underlying mechanism library must operate in order to 697 * get the user's credentials. This call is necessary since 698 * kwarnd runs as root and credentials are many times stored 699 * in files and directories specific to the user 700 */ 701 if (kwarnd_debug) 702 printf("app_krb5_user_uid called and returning uid = %d\n", 703 krb5_cc_uid); 704 return (krb5_cc_uid); 705 } 706 707 708 static bool_t 709 getpruid(char *pr, uid_t *uid) 710 { 711 char *rcp1 = NULL, *rcp2 = NULL, *rcp3 = NULL; 712 struct passwd *pw; 713 714 rcp1 = strdup(pr); 715 if (!rcp1) 716 return (FALSE); 717 rcp2 = strtok(rcp1, "@"); 718 rcp3 = strtok(rcp2, "/"); 719 720 if (rcp3) { 721 pw = getpwnam(rcp3); 722 *uid = pw->pw_uid; 723 free(rcp1); 724 return (TRUE); 725 } 726 727 free(rcp1); 728 return (FALSE); 729 } 730 731 732 static krb5_error_code 733 renew_creds( 734 char *princ, 735 time_t *new_exp_time) /* out */ 736 { 737 krb5_creds my_creds; 738 krb5_error_code code = 0; 739 struct k5_data k5; 740 741 uid_t saved_u = app_krb5_user_uid(); 742 uid_t u; 743 744 if (kwarnd_debug) 745 printf("renew start: uid=%d\n", app_krb5_user_uid()); 746 747 if (!getpruid(princ, &u)) { 748 if (kwarnd_debug) 749 printf("renew: getpruid failed, princ='%s'\n", 750 princ ? princ : "<null>"); 751 752 return (-1); /* better err num? */ 753 } 754 755 set_warnd_uid(u); 756 757 (void) memset(&my_creds, 0, sizeof (my_creds)); 758 (void) memset(&k5, 0, sizeof (k5)); 759 760 if (code = krb5_init_context(&k5.ctx)) { 761 com_err(progname, code, 762 gettext("while initializing Kerberos 5 library")); 763 goto out; 764 } 765 766 if ((code = krb5_cc_default(k5.ctx, &k5.cc))) { 767 com_err(progname, code, 768 gettext("while getting default ccache")); 769 goto out; 770 771 } 772 773 if ((code = krb5_parse_name(k5.ctx, princ, 774 &k5.me))) { 775 com_err(progname, code, gettext("when parsing name %s"), 776 princ); 777 goto out; 778 } 779 780 if ((code = krb5_get_renewed_creds(k5.ctx, &my_creds, k5.me, k5.cc, 781 NULL))) { 782 com_err(progname, code, gettext("while renewing creds")); 783 goto out; 784 } 785 786 if (code = krb5_cc_initialize(k5.ctx, k5.cc, k5.me)) { 787 com_err(progname, code, gettext("when initializing cache %s"), 788 "defcc"); 789 goto out; 790 } 791 792 if (code = krb5_cc_store_cred(k5.ctx, k5.cc, &my_creds)) { 793 com_err(progname, code, gettext("while storing credentials")); 794 goto out; 795 } 796 797 /* "return" new expire time */ 798 *new_exp_time = my_creds.times.endtime; 799 800 out: 801 krb5_free_cred_contents(k5.ctx, &my_creds); 802 803 if (k5.name) 804 krb5_free_unparsed_name(k5.ctx, k5.name); 805 if (k5.me) 806 krb5_free_principal(k5.ctx, k5.me); 807 if (k5.cc) 808 krb5_cc_close(k5.ctx, k5.cc); 809 if (k5.ctx) 810 krb5_free_context(k5.ctx); 811 812 set_warnd_uid(saved_u); 813 814 if (kwarnd_debug) 815 printf("renew end: code=%s, uid=%d\n", error_message(code), 816 app_krb5_user_uid()); 817 818 return (code); 819 } 820 821 static bool_t 822 loggedon(char *name) 823 { 824 register struct utmpx *ubuf; 825 char *rcp1 = NULL, *rcp2 = NULL, *rcp3 = NULL; 826 827 /* 828 * strip any realm or instance from principal so we can match 829 * against unix userid. 830 */ 831 rcp1 = strdup(name); 832 if (!rcp1) 833 return (FALSE); 834 rcp2 = strtok(rcp1, "@"); 835 rcp3 = strtok(rcp2, "/"); 836 837 /* 838 * Scan through the "utmpx" file for the 839 * entry for the person we want to send to. 840 */ 841 842 setutxent(); 843 while ((ubuf = getutxent()) != NULL) { 844 if (ubuf->ut_type == USER_PROCESS) { 845 if (strncmp(rcp3, ubuf->ut_user, 846 sizeof (ubuf->ut_user)) == 0) { 847 free(rcp1); 848 endutxent(); 849 return (TRUE); 850 851 } 852 } 853 } 854 free(rcp1); 855 endutxent(); 856 857 if (kwarnd_debug) 858 printf("loggedon: returning false for user `%s'\n", rcp1); 859 860 return (FALSE); 861 } 862 863 /* 864 * main loop to check the cred warning list and send the warnings 865 * the appropriate location based on warn.conf or auto-renew creds. 866 */ 867 868 void 869 kwarnd_check_warning_list(void) 870 { /* func */ 871 cred_warning_list_t *cw; /* cred warning */ 872 config_entry_list_t *ce; /* config entry */ 873 time_t now; 874 int minutes; 875 char buff[256]; 876 char cmdline[256]; 877 FILE *fp; 878 char *subj = "Kerberos credentials expiring"; 879 char *renew_subj = "Kerberos credentials renewed"; 880 881 if (kwarnd_debug) 882 printf("check list: start: uid=%d, cw list=%p\n", 883 app_krb5_user_uid(), cred_warning_list); 884 885 while (1) { 886 (void) poll(NULL, NULL, 60000); 887 888 for (cw = cred_warning_list; 889 cw != NULL; 890 cw = cw->next) { 891 int send_msg = 0; 892 893 time(&now); 894 if (now >= cw->cred_warn_time) { 895 int renew_attempted = 0; 896 int renew_failed = 0; 897 int renew_tooclose = 0; 898 899 if (kwarnd_debug) 900 printf("checklist: now >= warn_t\n"); 901 902 ce = find_warning_info(cw->warn_name); 903 minutes = (cw->cred_exp_time - 904 now + 59) / 60; 905 906 if (kwarnd_debug) 907 printf("checklist: where_to=%s\n", 908 ce->where_to ? 909 ce->where_to : "null"); 910 911 if (ce->renew && 912 loggedon(cw->warn_name)) { 913 krb5_error_code code; 914 time_t new_exp_time; 915 916 renew_attempted = 1; 917 code = renew_creds( 918 cw->warn_name, 919 &new_exp_time); 920 if (!code) { 921 /* krb5 api renew success */ 922 923 /* 924 * So we had api success 925 * but the new exp time 926 * is same as current one 927 * so we are too close 928 * to Renewable_life time. 929 */ 930 if (cw->cred_exp_time 931 == new_exp_time) { 932 renew_tooclose = 1; 933 if (kwarnd_debug) 934 printf( 935 "checklist: new expire time same as old expire time\n"); 936 937 if (ce->log_failure) { 938 send_msg = 1; 939 snprintf(buff, 940 sizeof (buff), 941 gettext("%s:\r\nYour kerberos" 942 " credentials have not been renewed" 943 " (too close to Renewable_life).\r\n" 944 "Please run kinit(1).\r\n"), 945 cw->warn_name); 946 } 947 } else { 948 /* update times */ 949 cw->cred_exp_time = 950 new_exp_time; 951 cw->cred_warn_time = 952 new_exp_time - 953 ce->seconds_to_warn; 954 } 955 956 if (kwarnd_debug) 957 printf( 958 "check list: new_w_t=%d\n", 959 cw->cred_warn_time); 960 961 if (!renew_tooclose && 962 ce->log_success) { 963 if (kwarnd_debug) 964 printf( 965 "check list: log success\n"); 966 967 send_msg = 1; 968 snprintf(buff, 969 sizeof (buff), 970 gettext("%s:\r\nYour kerberos" 971 " credentials have been renewed.\r\n"), 972 cw->warn_name); 973 } 974 975 } /* !(code) */ 976 977 if (!renew_tooclose && code && 978 ce->log_failure) { 979 if (kwarnd_debug) 980 printf( 981 "check list: log FAIL\n"); 982 983 send_msg = 1; 984 snprintf(buff, 985 sizeof (buff), 986 gettext("%s:\r\nYour kerberos" 987 " credentials failed to be renewed (%s).\r\n"), 988 cw->warn_name, 989 error_message(code)); 990 } 991 renew_failed = code ? 1 : 0; 992 993 } else if (minutes > 0) { 994 send_msg = 1; 995 snprintf(buff, sizeof (buff), 996 gettext("%s:\r\nyour kerberos" 997 " credentials expire in less than" 998 " %d minutes.\r\n"), 999 cw->warn_name, 1000 minutes); 1001 } else { 1002 send_msg = 1; 1003 snprintf(buff, sizeof (buff), 1004 gettext("%s:\r\nyour kerberos" 1005 " credentials have expired.\r\n"), 1006 cw->warn_name); 1007 } 1008 1009 if (kwarnd_debug) 1010 printf("checklist: send_msg=%d\n", 1011 send_msg); 1012 if (!send_msg) 1013 goto del_warning; 1014 1015 if (strncmp(ce->where_to, 1016 "mail", sizeof ("mail")) == 0) { 1017 char *argv[3]; 1018 1019 argv[0] = MAIL; 1020 (void) snprintf(cmdline, 1021 sizeof (cmdline), 1022 "%s", 1023 ce->email); 1024 argv[1] = cmdline; 1025 argv[2] = NULL; 1026 1027 fp = safe_popen_w(MAILPATH, argv); 1028 1029 if (fp) { 1030 1031 (void) fprintf(fp, 1032 "To: %s\nSubject: %s\n\n%s\n", 1033 ce->email, 1034 renew_attempted 1035 ? renew_subj : subj, 1036 buff); 1037 1038 fclose(fp); 1039 } else { 1040 syslog(LOG_ERR, 1041 gettext("could not fork " 1042 "mail program to e-mail " 1043 "warning to %s\n"), 1044 cmdline); 1045 } 1046 1047 } else if (strncmp(ce->where_to, 1048 "terminal", 1049 sizeof ("terminal")) == 0) { 1050 1051 warn_send(cw->warn_name, 1052 buff); 1053 1054 } else if (send_msg && strncmp(ce->where_to, 1055 "syslog", 1056 sizeof ("syslog")) == 0) { 1057 syslog(LOG_NOTICE|LOG_AUTH, 1058 "%s", 1059 buff); 1060 #if 0 1061 } else if (strncmp(ce->where_to, 1062 "snmp", 1063 sizeof ("snmp")) == 0) { 1064 #endif 1065 } else { 1066 if (kwarnd_debug) 1067 printf( 1068 "unknown msg method=`%s'\n", 1069 ce->where_to); 1070 1071 exit(1); 1072 } 1073 1074 del_warning: 1075 if (!renew_attempted || renew_failed || 1076 renew_tooclose) { 1077 if (del_warning_pvt(cw->warn_name) 1078 == TRUE) { 1079 1080 if (kwarnd_debug) 1081 printf( 1082 "check list: del warn succ\n"); 1083 1084 break; 1085 } else { 1086 if (kwarnd_debug) 1087 printf( 1088 "could not delete warning\n"); 1089 1090 syslog(LOG_ERR, gettext( 1091 "could not delete warning")); 1092 1093 exit(1); 1094 } 1095 } 1096 1097 } /* if (now) */ 1098 } /* for */ 1099 } /* while */ 1100 } /* func */ 1101