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