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