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