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