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