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