]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/realm.c
need automake as a build-dep, even though we don't use most of it
[1ts-debian.git] / zephyr / server / realm.c
1 #include "zserver.h"
2 #include <sys/socket.h>
3
4 Unacked *rlm_nacklist = NULL;   /* not acked list for realm-realm
5                                    packets */
6 ZRealm *otherrealms;             /* points to an array of the known
7                                    servers */
8 int nrealms = 0;                /* number of other realms */
9
10 /*
11  * External Routines:
12  *
13  * ZRealm *realm_which_realm(struct sockaddr_in *who)
14  * figures out if this packet came from another realm's server
15  *
16  * ZRealm *realm_get_realm_by_pid(int pid)
17  * figures out which realm a child handler was for
18  *
19  * void kill_realm_pids()
20  * kills all ticket getting childen
21  *
22  * char *realm_expand_realm(char *realmname)
23  * figures out what an abbreviated realm expands to
24  *
25  * Code_t realm_send_realms()
26  * loops through all realms for a brain dump
27  *
28  * int realm_bound_for_realm(char *realm, char *recip)
29  * figures out if recip is in realm, expanding recip's realm
30  *
31  * int realm_sender_in_realm(char *realm, char *sender)
32  * figures out if sender is in realm
33  *
34  * ZRealm *realm_get_realm_by_name(char *name)
35  * finds a realm struct from the realm array by name, tries expansion
36  *
37  * Code_t realm_dispatch(ZNotice_t *notice, int auth, struct sockaddr_in *who,
38  *                       Server *server)
39  * dispatches a message from a foreign realm
40  *
41  * void realm_init()
42  * sets up the realm module
43  *
44  * void realm_deathgram()
45  * tells other realms this server is going down
46  *
47  * void realm_wakeup()
48  * tells other realms to resend their idea of their subs to us
49  *
50  * Code_t realm_control_dispatch(ZNotice_t *notice, int auth,
51  *                               struct sockaddr_in *who, Server *server,
52  *                               ZRealm *realm)
53  * dispatches a foreign realm control message
54  *
55  * void realm_handoff(ZNotice_t *notice, int auth, struct sockaddr_in *who,
56  *                    ZRealm *realm, int ack_to_sender)
57  * hands off a message to another realm
58  *
59  * void realm_dump_realms(File *fp)
60  * do a database dump of foreign realm info
61  *
62  */
63 static void realm_sendit(ZNotice_t *notice, struct sockaddr_in *who, int auth, ZRealm *realm, int ack_to_sender);
64 #ifdef HAVE_KRB5
65 static Code_t realm_sendit_auth(ZNotice_t *notice, struct sockaddr_in *who, int auth, ZRealm *realm, int ack_to_sender);
66 #endif
67 static void rlm_ack(ZNotice_t *notice, Unacked *nacked);
68 static void rlm_nack_cancel(ZNotice_t *notice, struct sockaddr_in *who);
69 static void rlm_rexmit(void *arg);
70 static Code_t realm_ulocate_dispatch(ZNotice_t *notice,int auth,struct sockaddr_in *who,Server *server,ZRealm *realm);
71 static Code_t realm_new_server(struct sockaddr_in *, ZNotice_t *, ZRealm *);
72 static Code_t realm_set_server(struct sockaddr_in *, ZRealm *);
73 #ifdef HAVE_KRB5
74 static Code_t ticket_retrieve(ZRealm *realm);
75 static int ticket_lookup(char *realm);
76 #endif
77
78 static int
79 realm_get_idx_by_addr(ZRealm *realm,
80                       struct sockaddr_in *who)
81 {
82     struct sockaddr_in *addr;
83     int b;
84
85     /* loop through the realms */
86     for (addr = realm->addrs, b = 0; b < realm->count; b++, addr++)
87         if (addr->sin_addr.s_addr == who->sin_addr.s_addr)
88             return(b);
89
90     return 0;
91 }
92
93 const char *
94 realm_expand_realm(char *realmname)
95 {
96     ZRealm *realm;
97     int a;
98
99     /* First, look for an exact match (case insensitive) */
100 #if defined(HAVE_KRB4) || defined(HAVE_KRB5)
101     if (!strcasecmp(ZGetRealm(), realmname))
102         return(ZGetRealm());
103 #endif
104
105     for (realm = otherrealms, a = 0; a < nrealms; a++, realm++)
106       if (!strcasecmp(realm->name, realmname))
107         return(realm->name);
108
109     /* No exact match. See if there's a partial match */
110 #if defined(HAVE_KRB4) || defined(HAVE_KRB5)
111     if (!strncasecmp(ZGetRealm(), realmname, strlen(realmname)))
112         return(ZGetRealm());
113 #endif
114
115     for (realm = otherrealms, a = 0; a < nrealms; a++, realm++)
116         if (!strncasecmp(realm->name, realmname, strlen(realmname)))
117             return(realm->name);
118     return(realmname);
119 }
120
121 ZRealm *
122 realm_get_realm_by_pid(int pid)
123 {
124     ZRealm *realm;
125     int a;
126
127     for (realm = otherrealms, a = 0; a < nrealms; a++, realm++)
128         if (realm->child_pid == pid)
129             return(realm);
130
131     return 0;
132 }
133
134 void
135 kill_realm_pids(void)
136 {
137     ZRealm *realm;
138     int a;
139
140     for (realm = otherrealms, a = 0; a < nrealms; a++, realm++)
141         if (realm->child_pid != 0)
142             kill(realm->child_pid, 9);
143
144     return;
145 }
146
147 ZRealmname *
148 get_realm_lists(char *file)
149 {
150     ZRealmname *rlm_list, *rlm;
151     int ii, nused, ntotal;
152     FILE *fp;
153     char buf[REALM_SZ + NS_MAXDNAME + 1]; /* one for newline */
154     char realm[REALM_SZ], server[NS_MAXDNAME + 1];
155
156     nused = 0;
157     if (!(fp = fopen(file, "r")))
158         return((ZRealmname *)0);
159
160     /* start with 16, realloc if necessary */
161     ntotal = 16;
162     rlm_list = (ZRealmname *)malloc(ntotal * sizeof(ZRealmname));
163     if (!rlm_list) {
164         syslog(LOG_CRIT, "get_realm_lists malloc");
165         abort();
166     }
167
168     while (fgets(buf, sizeof(buf), fp)) {
169         if (sscanf(buf, "%s %s", realm, server) != 2) {
170             syslog(LOG_CRIT, "bad format in %s", file);
171             abort();
172         }
173         for (ii = 0; ii < nused; ii++) {
174             /* look for this realm */
175             if (!strcmp(rlm_list[ii].name, realm))
176                 break;
177         }
178         if (ii < nused) {
179             rlm = &rlm_list[ii];
180             if (rlm->nused +1 >= rlm->nservers) {
181                 /* make more space */
182                 rlm->servers = (char **)realloc((char *)rlm->servers,
183                                                 (unsigned)rlm->nservers * 2 *
184                                                 sizeof(char *));
185                 if (!rlm->servers) {
186                     syslog(LOG_CRIT, "get_realm_lists realloc");
187                     abort();
188                 }
189                 rlm->nservers *= 2;
190             }
191             rlm->servers[rlm->nused++] = strsave(server);
192         } else {
193             /* new realm */
194             if (nused + 1 >= ntotal) {
195                 /* make more space */
196                 rlm_list = (ZRealmname *)realloc((char *)rlm_list,
197                                                 (unsigned)ntotal * 2 *
198                                                 sizeof(ZRealmname));
199                 if (!rlm_list) {
200                     syslog(LOG_CRIT, "get_realm_lists realloc");
201                     abort();
202                 }
203                 ntotal *= 2;
204             }
205             rlm = &rlm_list[nused++];
206             strcpy(rlm->name, realm);
207             rlm->nused = 0;
208             rlm->nservers = 16;
209             rlm->servers = (char **)malloc(rlm->nservers * sizeof(char *));
210             if (!rlm->servers) {
211                 syslog(LOG_CRIT, "get_realm_lists malloc");
212                 abort();
213             }
214             rlm->servers[rlm->nused++] = strsave(server);
215         }
216     }
217     if (nused + 1 >= ntotal) {
218         rlm_list = (ZRealmname *)realloc((char *)rlm_list,
219                                         (unsigned)(ntotal + 1) *
220                                         sizeof(ZRealmname));
221         if (!rlm_list) {
222             syslog(LOG_CRIT, "get_realm_lists realloc");
223             abort();
224         }
225     }
226     *rlm_list[nused].name = '\0';
227
228     fclose(fp);
229     return(rlm_list);
230 }
231
232 Code_t
233 realm_send_realms(void)
234 {
235     int cnt, retval;
236     for (cnt = 0; cnt < nrealms; cnt++) {
237         retval = subscr_send_realm_subs(&otherrealms[cnt]);
238         if (retval != ZERR_NONE)
239             return(retval);
240     }
241     return ZERR_NONE;
242 }
243
244 int
245 realm_bound_for_realm(const char *realm, char *recip)
246 {
247     char *rlm = NULL;
248     int remote = strcmp(ZGetRealm(), realm);
249
250     if (recip)
251       rlm = strchr(recip, '@');
252
253     if (!rlm && !remote)
254         return 1;
255
256     if (rlm && strcmp(realm_expand_realm(rlm + 1), realm) == 0)
257         return 1;
258
259     return 0;
260 }
261
262 int
263 realm_sender_in_realm(const char *realm, char *sender)
264 {
265     char *rlm = NULL;
266     int remote = strcmp(ZGetRealm(), realm);
267
268     if (sender)
269         rlm = strchr(sender, '@');
270
271     if (!rlm && !remote)
272         return 1;
273
274     if (rlm && strcmp((rlm + 1), realm) == 0)
275         return 1;
276
277     return 0;
278 }
279
280 int sender_in_realm(ZNotice_t *notice)
281 {
282   char *realm;
283
284   realm = strchr(notice->z_sender, '@');
285
286   if (!realm || !strcmp(realm + 1, ZGetRealm()))
287     return 1;
288
289   return 0;
290 }
291
292 ZRealm *
293 realm_which_realm(struct sockaddr_in *who)
294 {
295     ZRealm *realm;
296     struct sockaddr_in *addr;
297     int a, b;
298
299     if (who->sin_port != srv_addr.sin_port)
300         return 0;
301
302     /* loop through the realms */
303     for (realm = otherrealms, a = 0; a < nrealms; a++, realm++)
304         /* loop through the addresses for the realm */
305         for (addr = realm->addrs, b = 0; b < realm->count; b++, addr++)
306             if (addr->sin_addr.s_addr == who->sin_addr.s_addr)
307                 return(realm);
308
309     return 0;
310 }
311
312 ZRealm *
313 realm_get_realm_by_name(char *name)
314 {
315     int a;
316     ZRealm *realm;
317
318     /* First, look for an exact match (case insensitive) */
319     for (realm = otherrealms, a = 0; a < nrealms; a++, realm++)
320         if (!strcasecmp(realm->name, name))
321             return(realm);
322
323     /* Failing that, look for an inexact match */
324     for (realm = otherrealms, a = 0; a < nrealms; a++, realm++)
325         if (!strncasecmp(realm->name, name, strlen(name)))
326             return(realm);
327
328     return 0;
329 }
330
331 static void
332 rlm_nack_cancel(register ZNotice_t *notice,
333                 struct sockaddr_in *who)
334 {
335     register ZRealm *which = realm_which_realm(who);
336     register Unacked *nacked;
337
338     zdbug((LOG_DEBUG, "rlm_nack_cancel: %s:%08X,%08X",
339            inet_ntoa(notice->z_uid.zuid_addr),
340            notice->z_uid.tv.tv_sec, notice->z_uid.tv.tv_usec));
341
342     if (!which) {
343         syslog(LOG_ERR, "non-realm ack?");
344         return;
345     }
346
347     for (nacked = rlm_nacklist; nacked; nacked = nacked->next) {
348         if (&otherrealms[nacked->dest.rlm.rlm_idx] == which) {
349             /* First, note the realm appears to be up */
350             which->state = REALM_UP;
351             if (ZCompareUID(&nacked->uid, &notice->z_uid)) {
352                 timer_reset(nacked->timer);
353
354                 if (nacked->ack_addr.sin_addr.s_addr)
355                     rlm_ack(notice, nacked);
356
357                 /* free the data */
358                 free(nacked->packet);
359                 Unacked_delete(nacked);
360                 free(nacked);
361                 return;
362             }
363         }
364     }
365     return;
366 }
367
368 static void
369 rlm_ack(ZNotice_t *notice,
370         Unacked *nacked)
371 {
372     ZNotice_t acknotice;
373     ZPacket_t ackpack;
374     int packlen;
375     Code_t retval;
376
377     /* tell the original sender the result */
378     acknotice = *notice;
379     acknotice.z_message_len = strlen(acknotice.z_message) + 1;
380
381     packlen = sizeof(ackpack);
382
383     if ((retval = ZFormatSmallRawNotice(&acknotice, ackpack, &packlen))
384         != ZERR_NONE) {
385         syslog(LOG_ERR, "rlm_ack format: %s",
386                error_message(retval));
387         return;
388     }
389     zdbug((LOG_DEBUG, "rlm_ack sending to %s/%d",
390            inet_ntoa(nacked->ack_addr.sin_addr),
391            ntohs(nacked->ack_addr.sin_port)));
392     if ((retval = ZSetDestAddr(&nacked->ack_addr)) != ZERR_NONE) {
393         syslog(LOG_WARNING, "rlm_ack set addr: %s",
394                error_message(retval));
395         return;
396     }
397     if ((retval = ZSendPacket(ackpack, packlen, 0)) != ZERR_NONE) {
398         syslog(LOG_WARNING, "rlm_ack xmit: %s", error_message(retval));
399         return;
400     }
401 }
402
403 Code_t
404 realm_dispatch(ZNotice_t *notice,
405                int auth,
406                struct sockaddr_in *who,
407                Server *server)
408 {
409     ZRealm *realm;
410     Code_t status = ZERR_NONE;
411     char rlm_recipient[REALM_SZ + 1];
412     int external = 0;
413     String *notice_class;
414
415     if (notice->z_kind == SERVACK || notice->z_kind == SERVNAK) {
416         rlm_nack_cancel(notice, who);
417         return(ZERR_NONE);
418     }
419
420     /* check if it's a control message */
421     realm = realm_which_realm(who);
422
423     notice_class = make_string(notice->z_class,1);
424
425     if (class_is_admin(notice_class)) {
426         syslog(LOG_WARNING, "%s sending admin opcode %s",
427                realm->name, notice->z_opcode);
428     } else if (class_is_hm(notice_class)) {
429         syslog(LOG_WARNING, "%s sending hm opcode %s",
430                realm->name, notice->z_opcode);
431     } else if (class_is_control(notice_class)) {
432         status = realm_control_dispatch(notice, auth, who,
433                                         server, realm);
434     } else if (class_is_ulogin(notice_class)) {
435         /* don't need to forward this */
436         if (server == me_server) {
437             sprintf(rlm_recipient, "@%s", realm->name);
438             notice->z_recipient = rlm_recipient;
439
440             sendit(notice, 1, who, 0);
441         }
442     } else if (class_is_ulocate(notice_class)) {
443         status = realm_ulocate_dispatch(notice, auth, who, server, realm);
444     } else {
445         /* redo the recipient */
446         if (*notice->z_recipient == '\0') {
447             sprintf(rlm_recipient, "@%s", realm->name);
448             notice->z_recipient = rlm_recipient;
449             external = 0;
450         } else if (realm_bound_for_realm(ZGetRealm(), notice->z_recipient)
451                    && *notice->z_recipient == '@')
452         {
453             /* we're responsible for getting this message out */
454             external = 1;
455             notice->z_recipient = "";
456         }
457
458         /* otherwise, send to local subscribers */
459         sendit(notice, auth, who, external);
460     }
461
462     return(status);
463 }
464
465 void
466 realm_init(void)
467 {
468     Client *client;
469     ZRealmname *rlmnames;
470     ZRealm *rlm;
471     int ii, jj, found;
472     struct in_addr *addresses;
473     struct hostent *hp;
474     char list_file[128];
475     char rlmprinc[MAX_PRINCIPAL_SIZE];
476
477     sprintf(list_file, "%s/zephyr/%s", SYSCONFDIR, REALM_LIST_FILE);
478     rlmnames = get_realm_lists(list_file);
479     if (!rlmnames) {
480         zdbug((LOG_DEBUG, "No other realms"));
481         nrealms = 0;
482         return;
483     }
484
485     for (nrealms = 0; *rlmnames[nrealms].name; nrealms++);
486
487     otherrealms = (ZRealm *)malloc(nrealms * sizeof(ZRealm));
488     if (!otherrealms) {
489         syslog(LOG_CRIT, "malloc failed in realm_init");
490         abort();
491     }
492
493     for (ii = 0; ii < nrealms; ii++) {
494         rlm = &otherrealms[ii];
495         strcpy(rlm->name, rlmnames[ii].name);
496
497         addresses = (struct in_addr *)malloc(rlmnames[ii].nused *
498                                              sizeof(struct in_addr));
499         if (!addresses) {
500             syslog(LOG_CRIT, "malloc failed in realm_init");
501             abort();
502         }
503         /* convert names to addresses */
504         found = 0;
505         for (jj = 0; jj < rlmnames[ii].nused; jj++) {
506             hp = gethostbyname(rlmnames[ii].servers[jj]);
507             if (hp) {
508                 memmove(&addresses[found], hp->h_addr,
509                         sizeof(struct in_addr));
510                 found++;
511             } else
512                 syslog(LOG_WARNING, "hostname failed, %s",
513                        rlmnames[ii].servers[jj]);
514             /* free the hostname */
515             free(rlmnames[ii].servers[jj]);
516         }
517         rlm->count = found;
518         rlm->addrs = (struct sockaddr_in *)malloc(found *
519                                                   sizeof (struct sockaddr_in));
520         if (!rlm->addrs) {
521             syslog(LOG_CRIT, "malloc failed in realm_init");
522             abort();
523         }
524         for (jj = 0; jj < rlm->count; jj++) {
525             rlm->addrs[jj].sin_family = AF_INET;
526             /* use the server port */
527             rlm->addrs[jj].sin_port = srv_addr.sin_port;
528             rlm->addrs[jj].sin_addr = addresses[jj];
529         }
530         client = (Client *) malloc(sizeof(Client));
531         if (!client) {
532             syslog(LOG_CRIT, "malloc failed in realm_init");
533             abort();
534         }
535         memset(&client->addr, 0, sizeof(struct sockaddr_in));
536 #ifdef HAVE_KRB5
537         client->session_keyblock = NULL;
538 #else
539 #ifdef HAVE_KRB4
540         memset(&client->session_key, 0, sizeof(client->session_key));
541 #endif
542 #endif
543         snprintf(rlmprinc, MAX_PRINCIPAL_SIZE, "%s.%s@%s", SERVER_SERVICE, SERVER_INSTANCE,
544                 rlm->name);
545         client->principal = make_string(rlmprinc, 0);
546         client->last_send = 0;
547         client->last_ack = NOW;
548         client->subs = NULL;
549         client->realm = rlm;
550         client->addr.sin_family = 0;
551         client->addr.sin_port = 0;
552         client->addr.sin_addr.s_addr = 0;
553
554         rlm->client = client;
555         rlm->idx = (rlm->count) ? random() % rlm->count : 0;
556         rlm->subs = NULL;
557         rlm->remsubs = NULL;
558         rlm->child_pid = 0;
559         /* Assume the best */
560         rlm->state = REALM_TARDY;
561         rlm->have_tkt = 1;
562         free(rlmnames[ii].servers);
563         free(addresses);
564     }
565     free(rlmnames);
566 }
567
568 void
569 realm_deathgram(Server *server)
570 {
571     ZRealm *realm;
572     int jj = 0;
573
574     /* Get it out once, and assume foreign servers will share */
575     for (realm = otherrealms, jj = 0; jj < nrealms; jj++, realm++) {
576         ZNotice_t snotice;
577         char *pack;
578         char rlm_recipient[REALM_SZ + 1];
579         int packlen, retval;
580
581         memset (&snotice, 0, sizeof (snotice));
582
583         snotice.z_kind = ACKED;
584         snotice.z_port = srv_addr.sin_port;
585         snotice.z_class = ZEPHYR_CTL_CLASS;
586         snotice.z_class_inst = ZEPHYR_CTL_REALM;
587         snotice.z_opcode = SERVER_SHUTDOWN;
588         snotice.z_sender = myname; /* my host name */
589         sprintf(rlm_recipient, "@%s", realm->name);
590         snotice.z_recipient = rlm_recipient;
591         snotice.z_default_format = "";
592         snotice.z_num_other_fields = 0;
593         snotice.z_default_format = "";
594         snotice.z_message = (server) ? server->addr_str : NULL;
595         snotice.z_message_len = (server) ? strlen(server->addr_str) + 1 : 0;
596
597         zdbug((LOG_DEBUG, "rlm_deathgram: suggesting %s to %s",
598                (server) ? server->addr_str : "nothing", realm->name));
599
600 #ifdef HAVE_KRB5
601         if (!ticket_lookup(realm->name))
602             if ((retval = ticket_retrieve(realm)) != ZERR_NONE) {
603                 syslog(LOG_WARNING, "rlm_deathgram failed: %s",
604                        error_message(retval));
605                 return;
606             }
607 #endif
608
609         if ((retval = ZFormatNotice(&snotice, &pack, &packlen, ZCAUTH))
610             != ZERR_NONE)
611         {
612             syslog(LOG_WARNING, "rlm_deathgram format: %s",
613                    error_message(retval));
614             return;
615         }
616         if ((retval = ZParseNotice(pack, packlen, &snotice)) != ZERR_NONE) {
617             syslog(LOG_WARNING, "rlm_deathgram parse: %s",
618                    error_message(retval));
619             free(pack);
620             return;
621         }
622
623         realm_handoff(&snotice, 1, NULL, realm, 0);
624         free(pack);
625     }
626 }
627
628 void
629 realm_wakeup(void)
630 {
631     int jj, found = 0;
632     ZRealm *realm;
633
634     for (jj = 1; jj < nservers; jj++) {    /* skip limbo server */
635         if (jj != me_server_idx && otherservers[jj].state == SERV_UP)
636             found++;
637     }
638
639     if (nservers < 2 || !found) {
640         /* if we're the only server up, send a REALM_BOOT to one of their
641            servers here */
642         for (realm = otherrealms, jj = 0; jj < nrealms; jj++, realm++) {
643             ZNotice_t snotice;
644             char *pack;
645             char rlm_recipient[REALM_SZ + 1];
646             int packlen, retval;
647
648             memset (&snotice, 0, sizeof (snotice));
649
650             snotice.z_opcode = REALM_BOOT;
651             snotice.z_port = srv_addr.sin_port;
652             snotice.z_class_inst = ZEPHYR_CTL_REALM;
653             snotice.z_class = ZEPHYR_CTL_CLASS;
654             snotice.z_recipient = "";
655             snotice.z_kind = ACKED;
656             snotice.z_num_other_fields = 0;
657             snotice.z_default_format = "";
658             snotice.z_sender = myname; /* my host name */
659             sprintf(rlm_recipient, "@%s", realm->name);
660             snotice.z_recipient = rlm_recipient;
661             snotice.z_default_format = "";
662             snotice.z_message = NULL;
663             snotice.z_message_len = 0;
664
665 #ifdef HAVE_KRB5
666             if (!ticket_lookup(realm->name))
667                 if ((retval = ticket_retrieve(realm)) != ZERR_NONE) {
668                     syslog(LOG_WARNING, "rlm_wakeup failed: %s",
669                            error_message(retval));
670                     continue;
671                 }
672 #endif
673
674             if ((retval = ZFormatNotice(&snotice, &pack, &packlen, ZAUTH))
675                 != ZERR_NONE)
676             {
677                 syslog(LOG_WARNING, "rlm_wakeup format: %s",
678                        error_message(retval));
679                 return;
680             }
681             if ((retval = ZParseNotice(pack, packlen, &snotice))
682                 != ZERR_NONE) {
683                 syslog(LOG_WARNING, "rlm_wakeup parse: %s",
684                        error_message(retval));
685                 free(pack);
686                 return;
687             }
688
689             realm_handoff(&snotice, 1, NULL, realm, 0);
690             free(pack);
691         }
692     }
693 }
694
695 static Code_t
696 realm_ulocate_dispatch(ZNotice_t *notice,
697                        int auth,
698                        struct sockaddr_in *who,
699                        Server *server,
700                        ZRealm *realm)
701 {
702     register char *opcode = notice->z_opcode;
703
704     if (!auth) {
705         syslog(LOG_WARNING, "unauth locate msg from %s (%s/%s/%s)",
706                inet_ntoa(who->sin_addr),
707                notice->z_class, notice->z_class_inst,
708                notice->z_opcode); /* XXX */
709         clt_ack(notice, who, AUTH_FAILED);
710         return(ZERR_NONE);
711     }
712
713     if (!strcmp(opcode, REALM_REQ_LOCATE)) {
714         ack(notice, who);
715         ulogin_realm_locate(notice, who, realm);
716     } else if (!strcmp(opcode, REALM_ANS_LOCATE)) {
717         ack(notice, who);
718         ulogin_relay_locate(notice, who);
719     } else {
720         syslog(LOG_WARNING, "%s unknown/illegal loc opcode %s",
721                realm->name, opcode);
722         nack(notice, who);
723     }
724
725     return(ZERR_NONE);
726 }
727
728
729 Code_t
730 realm_control_dispatch(ZNotice_t *notice,
731                        int auth,
732                        struct sockaddr_in *who,
733                        Server *server,
734                        ZRealm *realm)
735 {
736     register char *opcode = notice->z_opcode;
737     Code_t status;
738
739     if (!auth) {
740         syslog(LOG_WARNING, "unauth ctl msg from %s (%s/%s/%s)",
741                inet_ntoa(who->sin_addr),
742                notice->z_class, notice->z_class_inst,
743                notice->z_opcode); /* XXX */
744         if (server == me_server)
745             clt_ack(notice, who, AUTH_FAILED);
746         return(ZERR_NONE);
747     }
748
749     if (strcmp(notice->z_class_inst, ZEPHYR_CTL_REALM)) {
750         syslog(LOG_WARNING, "Invalid rlm_dispatch instance %s",
751                notice->z_class_inst);
752         return(ZERR_NONE);
753     }
754
755     if (!strcmp(opcode, REALM_REQ_SUBSCRIBE) || !strcmp(opcode, REALM_ADD_SUBSCRIBE)) {
756         /* try to add subscriptions */
757         /* attempts to get defaults are ignored */
758         if ((status = subscr_foreign_user(notice, who, server, realm)) != ZERR_NONE) {
759             clt_ack(notice, who, AUTH_FAILED);
760         } else if (server == me_server) {
761             server_forward(notice, auth, who);
762             ack(notice, who);
763         }
764     } else if (!strcmp(opcode, REALM_UNSUBSCRIBE)) {
765         /* try to remove subscriptions */
766         if ((status = subscr_realm_cancel(who, notice, realm)) != ZERR_NONE) {
767             clt_ack(notice, who, NOT_FOUND);
768         } else if (server == me_server) {
769             server_forward(notice, auth, who);
770             ack(notice, who);
771         }
772     } else if (!strcmp(opcode, REALM_BOOT)) {
773         zdbug((LOG_DEBUG, "got a REALM_BOOT from %d (me %d)", server, me_server));
774         realm->state = REALM_STARTING;
775         realm_set_server(who, realm);
776 #ifdef REALM_MGMT
777         /* resend subscriptions but only if this was to us */
778         if (server == me_server) {
779             if ((status = subscr_realm_subs(realm)) != ZERR_NONE) {
780                 clt_ack(notice, who, NOT_FOUND);
781             } else {
782                 /* do forward the hint in case it ever matters */
783                 server_forward(notice, auth, who);
784                 ack(notice, who);
785             }
786         }
787 #endif
788     } else if (!strcmp(opcode, SERVER_SHUTDOWN)) {
789         /* try to remove subscriptions */
790         if ((status = realm_new_server(who, notice, realm)) != ZERR_NONE) {
791             clt_ack(notice, who, NOT_FOUND);
792         } else if (server == me_server) {
793             server_forward(notice, auth, who);
794             ack(notice, who);
795         }
796     } else {
797         syslog(LOG_WARNING, "%s unknown/illegal ctl opcode %s",
798                realm->name, opcode);
799         if (server == me_server)
800             nack(notice, who);
801         return(ZERR_NONE);
802     }
803     return(ZERR_NONE);
804 }
805
806 static Code_t
807 realm_new_server(struct sockaddr_in *sin,
808                  ZNotice_t *notice,
809                  ZRealm *realm)
810 {
811     unsigned long addr;
812     ZRealm *rlm;
813     struct sockaddr_in sinaddr;
814     int srvidx;
815
816     if (!realm)
817         return ZSRV_NORLM;
818
819     srvidx = realm_get_idx_by_addr(realm, sin);
820     zdbug((LOG_DEBUG, "rlm_new_srv: message from %d in %s (%s)",
821            srvidx, realm->name, inet_ntoa(sin->sin_addr)));
822     if (realm->idx == srvidx) {
823         if (notice->z_message_len) {
824             addr = inet_addr(notice->z_message);
825             sinaddr.sin_addr.s_addr = addr;
826             rlm = realm_which_realm(&sinaddr);
827             /* Not exactly */
828             if (!rlm || (rlm != realm))
829                 return ZSRV_NORLM;
830             realm->idx = realm_get_idx_by_addr(realm, &sinaddr);
831         } else {
832             realm->idx = (realm->idx + 1) % realm->count;
833         }
834         zdbug((LOG_DEBUG, "rlm_new_srv: switched servers (%s)", inet_ntoa((realm->addrs[realm->idx]).sin_addr)));
835     } else {
836       zdbug((LOG_DEBUG, "rlm_new_srv: not switching servers (%s)", inet_ntoa((realm->addrs[realm->idx]).sin_addr)));
837     }
838     return 0;
839 }
840
841 static Code_t
842 realm_set_server(struct sockaddr_in *sin,
843                  ZRealm *realm)
844 {
845     ZRealm *rlm;
846
847     rlm = realm_which_realm(sin);
848     /* Not exactly */
849     if (!rlm || (rlm != realm))
850         return ZSRV_NORLM;
851     realm->idx = realm_get_idx_by_addr(realm, sin);
852     zdbug((LOG_DEBUG, "rlm_pick_srv: switched servers (%s)", inet_ntoa((realm->addrs[realm->idx]).sin_addr)));
853
854     return 0;
855 }
856
857 void
858 realm_handoff(ZNotice_t *notice,
859               int auth,
860               struct sockaddr_in *who,
861               ZRealm *realm,
862               int ack_to_sender)
863 {
864 #ifdef HAVE_KRB5
865     Code_t retval;
866
867     if (!auth) {
868         zdbug((LOG_DEBUG, "realm_sendit unauthentic to realm %s",
869                realm->name));
870         realm_sendit(notice, who, auth, realm, ack_to_sender);
871         return;
872     }
873
874     if (!ticket_lookup(realm->name))
875         if ((retval = ticket_retrieve(realm)) != ZERR_NONE) {
876             syslog(LOG_WARNING, "rlm_handoff failed: %s",
877                    error_message(retval));
878             realm_sendit(notice, who, auth, realm, ack_to_sender);
879             return;
880         }
881
882     zdbug((LOG_DEBUG, "realm_sendit to realm %s auth %d", realm->name, auth));
883     /* valid ticket available now, send the message */
884     retval = realm_sendit_auth(notice, who, auth, realm, ack_to_sender);
885 #else /* HAVE_KRB4 */
886     realm_sendit(notice, who, auth, realm, ack_to_sender);
887 #endif /* HAVE_KRB4 */
888 }
889
890 static void
891 realm_sendit(ZNotice_t *notice,
892              struct sockaddr_in *who,
893              int auth,
894              ZRealm *realm,
895              int ack_to_sender)
896 {
897     char *pack;
898     int packlen;
899     Code_t retval;
900     Unacked *nacked;
901
902     notice->z_auth = auth;
903
904     /* format the notice */
905     if ((retval = ZFormatRawNotice(notice, &pack, &packlen)) != ZERR_NONE) {
906         syslog(LOG_WARNING, "rlm_sendit format: %s",
907                error_message(retval));
908         return;
909     }
910
911     /* now send */
912     if ((retval = ZSetDestAddr(&realm->addrs[realm->idx])) != ZERR_NONE) {
913         syslog(LOG_WARNING, "rlm_sendit set addr: %s",
914                error_message(retval));
915         free(pack);
916         return;
917     }
918     if ((retval = ZSendPacket(pack, packlen, 0)) != ZERR_NONE) {
919         syslog(LOG_WARNING, "rlm_sendit xmit: %s", error_message(retval));
920         free(pack);
921         return;
922     }
923
924     /* now we've sent it, mark it as not ack'ed */
925
926     if (!(nacked = (Unacked *)malloc(sizeof(Unacked)))) {
927         /* no space: just punt */
928         syslog(LOG_ERR, "rlm_sendit nack malloc");
929         free(pack);
930         return;
931     }
932
933     memset(nacked, 0, sizeof(Unacked));
934     nacked->packet = pack;
935     nacked->dest.rlm.rlm_idx = realm - otherrealms;
936     nacked->dest.rlm.rlm_srv_idx = realm->idx;
937     nacked->packsz = packlen;
938     nacked->uid = notice->z_uid;
939     if (ack_to_sender)
940         nacked->ack_addr = *who;
941     else
942         nacked->ack_addr.sin_addr.s_addr = 0;
943
944     /* set a timer to retransmit */
945     nacked->timer = timer_set_rel(rexmit_times[0], rlm_rexmit, nacked);
946     /* chain in */
947     Unacked_insert(&rlm_nacklist, nacked);
948     return;
949 }
950
951 static void
952 packet_ctl_nack(Unacked *nackpacket)
953 {
954     ZNotice_t notice;
955
956     /* extract the notice */
957     ZParseNotice(nackpacket->packet, nackpacket->packsz, &notice);
958     if (nackpacket->ack_addr.sin_addr.s_addr != 0)
959         nack(&notice, &nackpacket->ack_addr);
960     else
961         syslog(LOG_WARNING, "would have acked nobody (%s/%s/%s)",
962                notice.z_class, notice.z_class_inst, notice.z_opcode); /* XXX */
963 }
964
965 static void
966 rlm_rexmit(void *arg)
967 {
968     Unacked *nackpacket = (Unacked *) arg;
969     Code_t retval;
970     register ZRealm *realm;
971
972     zdbug((LOG_DEBUG,"rlm_rexmit"));
973
974     realm = &otherrealms[nackpacket->dest.rlm.rlm_idx];
975
976     zdbug((LOG_DEBUG, "rlm_rexmit: sending to %s:%d (%d)",
977            realm->name, realm->idx, nackpacket->rexmits));
978
979     if (realm->count == 0)
980         return;
981
982     /* Check to see if we've retransmitted as many times as we can */
983     if (nackpacket->rexmits >= (NUM_REXMIT_TIMES * realm->count)) {
984         /* give a server ack that the packet is lost/realm dead */
985         packet_ctl_nack(nackpacket);
986         Unacked_delete(nackpacket);
987
988         zdbug((LOG_DEBUG, "rlm_rexmit: %s appears dead", realm->name));
989         realm->state = REALM_DEAD;
990
991         free(nackpacket->packet);
992         free(nackpacket);
993         return;
994     }
995
996     /* if we've reached our limit, move on to the next server */
997     if ((realm->state == REALM_TARDY) ||
998         (nackpacket->rexmits &&
999          !((nackpacket->rexmits+1) % (NUM_REXMIT_TIMES/3))))
1000     {
1001         realm->idx = (realm->idx + 1) % realm->count;
1002         zdbug((LOG_DEBUG, "rlm_rexmit: %s switching servers:%d (%s)",
1003                realm->name, realm->idx,
1004                inet_ntoa((realm->addrs[realm->idx]).sin_addr)));
1005     }
1006
1007     /* throttle back if it looks like the realm is down */
1008     if ((realm->state != REALM_DEAD) ||
1009         ((nackpacket->rexmits % (realm->count+1)) == 1)) {
1010         /* do the retransmit */
1011         retval = ZSetDestAddr(&realm->addrs[realm->idx]);
1012         if (retval != ZERR_NONE) {
1013             syslog(LOG_WARNING, "rlm_rexmit set addr: %s",
1014                    error_message(retval));
1015         } else {
1016             retval = ZSendPacket(nackpacket->packet, nackpacket->packsz, 0);
1017             if (retval != ZERR_NONE)
1018                 syslog(LOG_WARNING, "rlm_rexmit xmit: %s",
1019                        error_message(retval));
1020         }
1021         /* no per-server nack queues for foreign realms yet, doesn't matter */
1022         nackpacket->dest.rlm.rlm_srv_idx = realm->idx;
1023         zdbug((LOG_DEBUG, "rlm_rexmit(%s): send to %s", realm->name,
1024                inet_ntoa((realm->addrs[realm->idx]).sin_addr)));
1025     } else {
1026         zdbug((LOG_DEBUG, "rlm_rexmit(%s): not sending to %s", realm->name,
1027                inet_ntoa((realm->addrs[realm->idx]).sin_addr)));
1028     }
1029
1030     /* reset the timer */
1031     nackpacket->rexmits++;
1032     nackpacket->timer =
1033         timer_set_rel(rexmit_times[nackpacket->rexmits%NUM_REXMIT_TIMES],
1034                       rlm_rexmit, nackpacket);
1035     if (rexmit_times[nackpacket->rexmits%NUM_REXMIT_TIMES] == -1)
1036         zdbug((LOG_DEBUG, "rlm_rexmit(%s): would send at -1 to %s",
1037                realm->name, inet_ntoa((realm->addrs[realm->idx]).sin_addr)));
1038
1039     return;
1040 }
1041
1042 void
1043 realm_dump_realms(FILE *fp)
1044 {
1045     register int ii, jj;
1046
1047     for (ii = 0; ii < nrealms; ii++) {
1048         (void) fprintf(fp, "%d:%s\n", ii, otherrealms[ii].name);
1049         for (jj = 0; jj < otherrealms[ii].count; jj++) {
1050             (void) fprintf(fp, "\t%s\n",
1051                            inet_ntoa(otherrealms[ii].addrs[jj].sin_addr));
1052         }
1053         /* dump the subs */
1054         subscr_dump_subs(fp, otherrealms[ii].subs);
1055     }
1056 }
1057
1058 #ifdef HAVE_KRB5
1059
1060 static Code_t
1061 realm_auth_sendit_nacked(char *buffer, int packlen, ZRealm *realm,
1062                          ZUnique_Id_t uid, int ack_to_sender,
1063                          struct sockaddr_in *who)
1064 {
1065     Unacked *nacked;
1066
1067     nacked = (Unacked *) malloc(sizeof(Unacked));
1068     if (nacked == NULL)
1069         return ENOMEM;
1070
1071     memset(nacked, 0, sizeof(Unacked));
1072     nacked->packet = buffer;
1073     nacked->dest.rlm.rlm_idx = realm - otherrealms;
1074     nacked->dest.rlm.rlm_srv_idx = realm->idx;
1075     nacked->packsz = packlen;
1076     nacked->uid = uid;
1077
1078     /* Do the ack for the last frag, below */
1079     if (ack_to_sender)
1080         nacked->ack_addr = *who;
1081     else
1082         nacked->ack_addr.sin_addr.s_addr = 0;
1083
1084     /* set a timer to retransmit */
1085     nacked->timer = timer_set_rel(rexmit_times[0], rlm_rexmit, nacked);
1086
1087     /* chain in */
1088     Unacked_insert(&rlm_nacklist, nacked);
1089
1090     return ZERR_NONE;
1091 }
1092
1093 static Code_t
1094 realm_sendit_auth(ZNotice_t *notice,
1095                   struct sockaddr_in *who,
1096                   int auth,
1097                   ZRealm *realm,
1098                   int ack_to_sender)
1099 {
1100     char *buffer = NULL;
1101     int hdrlen, offset, fragsize, message_len;
1102     int origoffset, origlen;
1103     Code_t retval;
1104     char multi[64];
1105     ZNotice_t partnotice, newnotice;
1106
1107     offset = 0;
1108
1109     buffer = (char *)malloc(sizeof(ZPacket_t));
1110     if (!buffer) {
1111         syslog(LOG_ERR, "realm_sendit_auth malloc");
1112         return ENOMEM; /* DON'T put on nack list */
1113     }
1114
1115     newnotice = *notice;
1116
1117     hdrlen = 0;
1118     retval = ZMakeZcodeRealmAuthentication(&newnotice, buffer, sizeof(ZPacket_t),
1119                                            &hdrlen, realm->name);
1120     if (retval)
1121         syslog(LOG_WARNING,
1122                "rlm_sendit_auth: ZMakeZcodeRealmAuthentication: %s",
1123                error_message(retval));
1124
1125     if (!retval) {
1126         retval = ZSetDestAddr(&realm->addrs[realm->idx]);
1127         if (retval)
1128             syslog(LOG_WARNING, "rlm_sendit_auth: ZSetDestAddr: %s",
1129                    error_message(retval));
1130     }
1131
1132     /* This is not terribly pretty, but it does do its job.
1133      * If a packet we get that needs to get sent off to another realm is
1134      * too big after we slap on our authent, we refragment it further,
1135      * a la Z_SendFragmentedNotice. This obviates the need for what
1136      * used to be done in ZFormatAuthenticRealmNotice, as we do it here.
1137      * At some point it should be pulled back out into its own function,
1138      * but only the server uses it.
1139      */
1140
1141     if (!retval &&
1142         ((notice->z_message_len+hdrlen > sizeof(ZPacket_t)) ||
1143          (notice->z_message_len+hdrlen > Z_MAXPKTLEN))) {
1144
1145         /* Reallocate buffers inside the refragmenter */
1146         free(buffer);
1147         buffer = NULL;
1148
1149         partnotice = *notice;
1150
1151         origoffset = 0;
1152         origlen = notice->z_message_len;
1153
1154         if (notice->z_multinotice && strcmp(notice->z_multinotice, "")) {
1155             if (sscanf(notice->z_multinotice, "%d/%d", &origoffset,
1156                        &origlen) != 2) {
1157                 syslog(LOG_WARNING,
1158                        "rlm_sendit_auth frag: multinotice parse failed");
1159                 retval = ZERR_BADFIELD;
1160             }
1161         }
1162
1163         fragsize = Z_MAXPKTLEN - hdrlen - Z_FRAGFUDGE;
1164
1165         if (fragsize < 0)
1166             retval = ZERR_HEADERLEN;
1167
1168         while (!retval &&
1169                (offset < notice->z_message_len || !notice->z_message_len)) {
1170             (void)sprintf(multi, "%d/%d", offset+origoffset, origlen);
1171             partnotice.z_multinotice = multi;
1172             if (offset > 0) {
1173                 (void)Z_gettimeofday(&partnotice.z_uid.tv,
1174                                       (struct timezone *)0);
1175                 partnotice.z_uid.tv.tv_sec = htonl((u_long)
1176                                                    partnotice.z_uid.tv.tv_sec);
1177                 partnotice.z_uid.tv.tv_usec =
1178                     htonl((u_long) partnotice.z_uid.tv.tv_usec);
1179                 (void)memcpy((char *)&partnotice.z_uid.zuid_addr, &__My_addr,
1180                               sizeof(__My_addr));
1181                 partnotice.z_sender_sockaddr.ip4.sin_family = AF_INET; /* XXX */
1182                 (void)memcpy((char *)&partnotice.z_sender_sockaddr.ip4.sin_addr,
1183                               &__My_addr, sizeof(__My_addr));
1184             }
1185             message_len = min(notice->z_message_len-offset, fragsize);
1186             partnotice.z_message = notice->z_message+offset;
1187             partnotice.z_message_len = message_len;
1188
1189             buffer = (char *)malloc(sizeof(ZPacket_t));
1190             if (!buffer) {
1191                 syslog(LOG_ERR, "realm_sendit_auth malloc");
1192                 retval = ENOMEM; /* DON'T put on nack list */
1193             }
1194
1195             if (!retval) {
1196                 retval = ZMakeZcodeRealmAuthentication(&partnotice, buffer,
1197                                                        sizeof(ZPacket_t),
1198                                                        &hdrlen,
1199                                                        realm->name);
1200                 if (retval != ZERR_NONE)
1201                     syslog(LOG_WARNING, "rlm_sendit_auth set addr: %s",
1202                            error_message(retval));
1203             }
1204
1205             if (!retval) {
1206                 (void) memcpy(buffer + hdrlen, partnotice.z_message,
1207                               partnotice.z_message_len);
1208
1209                 retval = ZSendPacket(buffer,
1210                                      hdrlen + partnotice.z_message_len, 0);
1211                 if (retval)
1212                     syslog(LOG_WARNING, "rlm_sendit_auth xmit: %s",
1213                            error_message(retval));
1214             }
1215
1216             if (!retval) {
1217                 retval = realm_auth_sendit_nacked(buffer, hdrlen +
1218                                                 partnotice.z_message_len,realm,
1219                                                   partnotice.z_uid,
1220                                                   ack_to_sender, who);
1221                 if (retval) /* no space: just punt */
1222                     syslog(LOG_ERR,
1223                            "rlm_sendit_auth: realm_auth_sendit_nacked: %s",
1224                            error_message(retval));
1225             }
1226
1227             if (!retval)
1228                 offset += fragsize;
1229
1230             if (!notice->z_message_len)
1231                 break;
1232         }
1233     } else if (!retval) {
1234         /* This is easy, no further fragmentation needed */
1235         (void)memcpy(buffer + hdrlen, newnotice.z_message,
1236                       newnotice.z_message_len);
1237
1238         retval = ZSendPacket(buffer, hdrlen + newnotice.z_message_len, 0);
1239         if (retval)
1240             syslog(LOG_WARNING, "rlm_sendit_auth xmit: %s",
1241                    error_message(retval));
1242         else {
1243             retval = realm_auth_sendit_nacked(buffer,
1244                                               hdrlen + newnotice.z_message_len,
1245                                               realm, newnotice.z_uid,
1246                                               ack_to_sender, who);
1247             if (retval) /* no space: just punt */
1248                 syslog(LOG_ERR, "rlm_sendit_auth: realm_auth_sendit_nacked: %s",
1249                        error_message(retval));
1250         }
1251     }
1252
1253     if (retval && buffer != NULL)
1254         free(buffer);
1255     return retval;
1256 }
1257
1258 static int
1259 ticket_lookup(char *realm)
1260 {
1261     krb5_error_code result;
1262     krb5_timestamp sec;
1263     krb5_ccache ccache;
1264     krb5_creds creds_in, *creds;
1265
1266     result = krb5_cc_default(Z_krb5_ctx, &ccache);
1267     if (result)
1268       return 0;
1269
1270     memset(&creds_in, 0, sizeof(creds_in));
1271
1272     result = krb5_cc_get_principal(Z_krb5_ctx, ccache, &creds_in.client);
1273     if (result) {
1274       krb5_cc_close(Z_krb5_ctx, ccache);
1275       return 0;
1276     }
1277
1278     result = krb5_build_principal(Z_krb5_ctx, &creds_in.server,
1279                                   strlen(realm),
1280                                   realm,
1281                                   SERVER_KRB5_SERVICE, SERVER_INSTANCE,
1282                                   NULL);
1283     if (result) {
1284       krb5_cc_close(Z_krb5_ctx, ccache);
1285       return 0;
1286     }
1287
1288     result = krb5_get_credentials(Z_krb5_ctx, 0 /* flags */, ccache,
1289                                   &creds_in, &creds);
1290     krb5_cc_close(Z_krb5_ctx, ccache);
1291     /* good ticket? */
1292
1293     krb5_timeofday (Z_krb5_ctx, &sec);
1294     krb5_free_cred_contents(Z_krb5_ctx, &creds_in); /* hope this is OK */
1295     if ((result == 0) && (sec < creds->times.endtime)) {
1296       krb5_free_creds(Z_krb5_ctx, creds);
1297       return (1);
1298     }
1299     if (!result) krb5_free_creds(Z_krb5_ctx, creds);
1300
1301     return (0);
1302 }
1303
1304 static Code_t
1305 ticket_retrieve(ZRealm *realm)
1306 {
1307     int pid;
1308     krb5_ccache ccache;
1309     krb5_error_code result;
1310     krb5_creds creds_in, *creds;
1311
1312     get_tgt();
1313
1314     if (realm->child_pid)
1315         /* Right idea. Basically, we haven't gotten it yet */
1316         return KRB5KRB_AP_ERR_TKT_EXPIRED;
1317
1318     if (realm->have_tkt) {
1319         /* Get a pointer to the default ccache. We don't need to free this. */
1320         result = krb5_cc_default(Z_krb5_ctx, &ccache);
1321
1322         /* GRRR.  There's no allocator or constructor for krb5_creds */
1323         /* GRRR.  It would be nice if this API were documented at all */
1324         memset(&creds_in, 0, sizeof(creds_in));
1325
1326         if (!result)
1327             result = krb5_cc_get_principal(Z_krb5_ctx, ccache, &creds_in.client);
1328         /* construct the service principal */
1329         if (!result)
1330             result = krb5_build_principal(Z_krb5_ctx, &creds_in.server,
1331                                           strlen(realm->name), realm->name,
1332                                           SERVER_KRB5_SERVICE, SERVER_INSTANCE,
1333                                           NULL);
1334
1335         /* HOLDING: creds_in.server */
1336
1337         /* look up or get the credentials we need */
1338         if (!result)
1339             result = krb5_get_credentials(Z_krb5_ctx, 0 /* flags */, ccache,
1340                                           &creds_in, &creds);
1341         krb5_cc_close(Z_krb5_ctx, ccache);
1342         krb5_free_cred_contents(Z_krb5_ctx, &creds_in); /* hope this is OK */
1343         if (!result) {
1344             krb5_free_creds(Z_krb5_ctx, creds);
1345             return 0;
1346         }
1347     } else {
1348         syslog(LOG_ERR, "tkt_rtrv: don't have ticket, but have no child");
1349         result = KRB5KRB_AP_ERR_TKT_EXPIRED;
1350     }
1351
1352     pid = fork();
1353     if (pid < 0) {
1354         syslog(LOG_ERR, "tkt_rtrv: can't fork");
1355         return errno;
1356     }
1357     else if (pid == 0) {
1358 #ifdef _POSIX_VERSION
1359         struct sigaction action;
1360
1361         action.sa_flags = 0;
1362         sigemptyset(&action.sa_mask);
1363         action.sa_handler = 0;
1364         sigaction(SIGCHLD, &action, NULL);
1365         sigaction(SIGINT, &action, NULL);
1366         sigaction(SIGTERM, &action, NULL);
1367         sigaction(SIGUSR1, &action, NULL);
1368         sigaction(SIGUSR2, &action, NULL);
1369         sigaction(SIGFPE, &action, NULL);
1370         sigaction(SIGHUP, &action, NULL);
1371 #ifdef SIGEMT
1372         sigaction(SIGEMT, &action, NULL);
1373 #endif
1374 #else
1375         signal(SIGCHLD, SIG_DFL);
1376         signal(SIGINT, SIG_DFL);
1377         signal(SIGTERM, SIG_DFL);
1378         signal(SIGUSR1, SIG_DFL);
1379         signal(SIGUSR2, SIG_DFL);
1380         signal(SIGFPE, SIG_DFL);
1381         signal(SIGHUP, SIG_DFL);
1382 #ifdef SIGEMT
1383         signal(SIGEMT, SIG_DFL);
1384 #endif
1385 #endif
1386
1387         syslog(LOG_INFO, "tkt_rtrv running for %s", realm->name);
1388         while (1) {
1389             /* Get a pointer to the default ccache.
1390                We don't need to free this. */
1391             result = krb5_cc_default(Z_krb5_ctx, &ccache);
1392
1393             /* GRRR.  There's no allocator or constructor for krb5_creds */
1394             /* GRRR.  It would be nice if this API were documented at all */
1395             memset(&creds_in, 0, sizeof(creds_in));
1396
1397             if (!result)
1398                 result = krb5_cc_get_principal(Z_krb5_ctx, ccache,
1399                                                &creds_in.client);
1400             /* construct the service principal */
1401             if (!result)
1402                 result = krb5_build_principal(Z_krb5_ctx, &creds_in.server,
1403                                               strlen(realm->name), realm->name,
1404                                               SERVER_KRB5_SERVICE,
1405                                               SERVER_INSTANCE,
1406                                               NULL);
1407
1408             /* HOLDING: creds_in.server */
1409
1410             /* look up or get the credentials we need */
1411             if (!result)
1412                 result = krb5_get_credentials(Z_krb5_ctx, 0 /* flags */, ccache,
1413                                               &creds_in, &creds);
1414             krb5_cc_close(Z_krb5_ctx, ccache);
1415             krb5_free_cred_contents(Z_krb5_ctx, &creds_in); /* hope this is OK */
1416             if (!result) {
1417                 krb5_free_creds(Z_krb5_ctx, creds);
1418                 syslog(LOG_INFO, "tkt_rtrv succeeded for %s", realm->name);
1419                 exit(0);
1420             }
1421
1422             /* Sleep a little while before retrying */
1423             sleep(30);
1424         }
1425     } else {
1426         realm->child_pid = pid;
1427         realm->have_tkt = 0;
1428
1429         syslog(LOG_WARNING, "tkt_rtrv: %s: %d", realm->name,
1430                result);
1431         return (result);
1432     }
1433 }
1434 #endif /* HAVE_KRB5 */