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 *
find_cred_warning(WARNING_NAME_T warn_name)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
kwarn_add_warning_1_svc(kwarn_add_warning_arg * argp,kwarn_add_warning_res * res,struct svc_req * rqstp)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
kwarn_del_warning_1_svc(kwarn_del_warning_arg * argp,kwarn_del_warning_res * res,struct svc_req * rqstp)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
del_warning_pvt(char * warning_name)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
loadConfigFile(void)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
cmp_renew_opts(char * opt,int * log_success,int * log_failure)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
parseConfigLine(char * buffer)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 *
find_warning_info(char * principal)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 *
safe_popen_w(char * path_to_cmd,char ** argv)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
set_warnd_uid(uid_t uid)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
app_krb5_user_uid(void)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
getpruid(char * pr,uid_t * uid)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
renew_creds(char * princ,time_t * new_exp_time)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
loggedon(char * name)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
kwarnd_check_warning_list(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