#include "zserver.h" #include Unacked *rlm_nacklist = NULL; /* not acked list for realm-realm packets */ Realm *otherrealms; /* points to an array of the known servers */ int nrealms = 0; /* number of other realms */ /* * External Routines: * * Realm *realm_which_realm(struct sockaddr_in *who) * figures out if this packet came from another realm's server * * Realm *realm_get_realm_by_pid(int pid) * figures out which realm a child handler was for * * void kill_realm_pids() * kills all ticket getting childen * * char *realm_expand_realm(char *realmname) * figures out what an abbreviated realm expands to * * Code_t realm_send_realms() * loops through all realms for a brain dump * * int realm_sender_in_realm(char *realm, char *sender) * figures out if sender is in realm * * Realm *realm_get_realm_by_name(char *name) * finds a realm struct from the realm array by name, tries expansion * * Code_t realm_dispatch(ZNotice_t *notice, int auth, struct sockaddr_in *who, * Server *server) * dispatches a message from a foreign realm * * void realm_init() * sets up the realm module * * void realm_deathgram() * tells other realms this server is going down * * void realm_wakeup() * tells other realms to resend their idea of their subs to us * * Code_t realm_control_dispatch(ZNotice_t *notice, int auth, * struct sockaddr_in *who, Server *server, * Realm *realm) * dispatches a foreign realm control message * * void realm_handoff(ZNotice_t *notice, int auth, struct sockaddr_in *who, * Realm *realm, int ack_to_sender) * hands off a message to another realm * * void realm_dump_realms(File *fp) * do a database dump of foreign realm info * */ static void realm_sendit __P((ZNotice_t *notice, struct sockaddr_in *who, int auth, Realm *realm, int ack_to_sender)); static void realm_sendit_auth __P((ZNotice_t *notice, struct sockaddr_in *who, int auth, Realm *realm, int ack_to_sender)); static void rlm_ack __P((ZNotice_t *notice, Unacked *nacked)); static void rlm_nack_cancel __P((ZNotice_t *notice, struct sockaddr_in *who)); static void rlm_new_ticket __P(()); static void rlm_rexmit __P((void *arg)); static Code_t realm_ulocate_dispatch __P((ZNotice_t *notice,int auth,struct sockaddr_in *who,Server *server,Realm *realm)); static Code_t realm_new_server __P((struct sockaddr_in *, ZNotice_t *, Realm *)); static Code_t realm_set_server __P((struct sockaddr_in *, Realm *)); #ifdef HAVE_KRB4 static Code_t ticket_retrieve __P((Realm *realm)); static int ticket_lookup __P((char *realm)); static int ticket_expired __P((CREDENTIALS *cred)); #endif static int realm_get_idx_by_addr(realm, who) Realm *realm; struct sockaddr_in *who; { struct sockaddr_in *addr; int a, b; /* loop through the realms */ for (addr = realm->addrs, b = 0; b < realm->count; b++, addr++) if (addr->sin_addr.s_addr == who->sin_addr.s_addr) return(b); return 0; } char * realm_expand_realm(realmname) char *realmname; { Realm *realm; int a; /* First, look for an exact match (case insensitive) */ if (!strcasecmp(my_galaxy, realmname)) return(my_galaxy); for (realm = otherrealms, a = 0; a < nrealms; a++, realm++) if (!strcasecmp(realm->name, realmname)) return(realm->name); /* No exact match. See if there's a partial match */ if (!strncasecmp(my_galaxy, realmname, strlen(realmname))) return(my_galaxy); for (realm = otherrealms, a = 0; a < nrealms; a++, realm++) if (!strncasecmp(realm->name, realmname, strlen(realmname))) return(realm->name); return(realmname); } Realm * realm_get_realm_by_pid(pid) int pid; { Realm *realm; int a; for (realm = otherrealms, a = 0; a < nrealms; a++, realm++) if (realm->child_pid == pid) return(realm); return 0; } void kill_realm_pids() { Realm *realm; int a; for (realm = otherrealms, a = 0; a < nrealms; a++, realm++) if (realm->child_pid != 0) kill(realm->child_pid, 9); return; } Code_t realm_send_realms() { int cnt, retval; for (cnt = 0; cnt < nrealms; cnt++) { if (retval = (subscr_send_realm_subs(&otherrealms[cnt])) != ZERR_NONE) return(retval); } } int realm_sender_in_realm(realm, sender) char *realm; char *sender; { char *senderrealm = NULL; int local = (strcmp(my_galaxy, realm) == 0); if (sender) senderrealm = strrchr(sender, '@'); if (!senderrealm && local) return 1; if (senderrealm && strcmp(senderrealm + 1, realm) == 0) return 1; return 0; } sender_in_realm(notice) ZNotice_t *notice; { char *realm; realm = strchr(notice->z_sender, '@'); if (!realm || !strcmp(realm + 1, my_galaxy)) return 1; return 0; } Realm * realm_which_realm(who) struct sockaddr_in *who; { Realm *realm; struct sockaddr_in *addr; int a, b; if (who->sin_port != srv_addr.sin_port) return 0; /* loop through the realms */ for (realm = otherrealms, a = 0; a < nrealms; a++, realm++) /* loop through the addresses for the realm */ for (addr = realm->addrs, b = 0; b < realm->count; b++, addr++) if (addr->sin_addr.s_addr == who->sin_addr.s_addr) return(realm); return 0; } Realm * realm_get_realm_by_name(name) char *name; { int a; Realm *realm; /* First, look for an exact match (case insensitive) */ for (realm = otherrealms, a = 0; a < nrealms; a++, realm++) if (!strcasecmp(realm->name, name)) return(realm); /* Failing that, look for an inexact match */ for (realm = otherrealms, a = 0; a < nrealms; a++, realm++) if (!strncasecmp(realm->name, name, strlen(name))) return(realm); return 0; } static void rlm_nack_cancel(notice, who) register ZNotice_t *notice; struct sockaddr_in *who; { register Realm *which = realm_which_realm(who); register Unacked *nacked, *next; ZPacket_t retval; #if 1 zdbug((LOG_DEBUG, "rlm_nack_cancel: %s:%08X,%08X", inet_ntoa(notice->z_uid.zuid_addr), notice->z_uid.tv.tv_sec, notice->z_uid.tv.tv_usec)); #endif if (!which) { syslog(LOG_ERR, "non-realm ack?"); return; } for (nacked = rlm_nacklist; nacked; nacked = nacked->next) { if (&otherrealms[nacked->dest.rlm.rlm_idx] == which) { /* First, note the realm appears to be up */ which->state = REALM_UP; if (ZCompareUID(&nacked->uid, ¬ice->z_uid)) { timer_reset(nacked->timer); if (nacked->ack_addr.sin_addr.s_addr) rlm_ack(notice, nacked); /* free the data */ free(nacked->packet); LIST_DELETE(nacked); free(nacked); return; } } } #if 0 zdbug((LOG_DEBUG,"nack_cancel: nack not found %s:%08X,%08X", inet_ntoa (notice->z_uid.zuid_addr), notice->z_uid.tv.tv_sec, notice->z_uid.tv.tv_usec)); #endif return; } static void rlm_ack(notice, nacked) ZNotice_t *notice; Unacked *nacked; { ZNotice_t acknotice; ZPacket_t ackpack; int packlen; Code_t retval; /* tell the original sender the result */ acknotice = *notice; acknotice.z_message_len = strlen(acknotice.z_message) + 1; packlen = sizeof(ackpack); if ((retval = ZFormatSmallRawNotice(&acknotice, ackpack, &packlen)) != ZERR_NONE) { syslog(LOG_ERR, "rlm_ack format: %s", error_message(retval)); return; } zdbug((LOG_DEBUG, "rlm_ack sending to %s/%d", inet_ntoa(nacked->ack_addr.sin_addr), ntohs(nacked->ack_addr.sin_port))); if ((retval = ZSetDestAddr(&nacked->ack_addr)) != ZERR_NONE) { syslog(LOG_WARNING, "rlm_ack set addr: %s", error_message(retval)); return; } if ((retval = ZSendPacket(ackpack, packlen, 0)) != ZERR_NONE) { syslog(LOG_WARNING, "rlm_ack xmit: %s", error_message(retval)); return; } } Code_t realm_dispatch(notice, auth, who, server) ZNotice_t *notice; int auth; struct sockaddr_in *who; Server *server; { Realm *realm; struct sockaddr_in newwho; Code_t status = ZERR_NONE; char rlm_recipient[REALM_SZ + 1]; int external = 0; String *notice_class; if (notice->z_kind == SERVACK || notice->z_kind == SERVNAK) { rlm_nack_cancel(notice, who); return(ZERR_NONE); } /* set up a who for the real origin */ memset((caddr_t) &newwho, 0, sizeof(newwho)); newwho.sin_family = AF_INET; newwho.sin_addr.s_addr = notice->z_sender_addr.s_addr; newwho.sin_port = hm_port; /* check if it's a control message */ realm = realm_which_realm(who); notice_class = make_string(notice->z_class,1); if (class_is_admin(notice_class)) { syslog(LOG_WARNING, "%s sending admin opcode %s", realm->name, notice->z_opcode); } else if (class_is_hm(notice_class)) { syslog(LOG_WARNING, "%s sending hm opcode %s", realm->name, notice->z_opcode); } else if (class_is_control(notice_class)) { status = realm_control_dispatch(notice, auth, who, server, realm); } else if (class_is_ulogin(notice_class)) { /* don't need to forward this */ if (server == me_server) { sprintf(rlm_recipient, "@%s", realm->name); notice->z_recipient = rlm_recipient; sendit(notice, 1, who, 0, 0); } } else if (class_is_ulocate(notice_class)) { status = realm_ulocate_dispatch(notice, auth, who, server, realm); } else { /* redo the recipient */ if (*notice->z_recipient == '\0') { sprintf(rlm_recipient, "@%s", realm->name); notice->z_recipient = rlm_recipient; external = 0; } else if ((*notice->z_recipient == '@') && (strcmp(realm_expand_realm(notice->z_recipient+1), my_galaxy) == 0)) { /* we're responsible for getting this message out */ external = 1; notice->z_recipient = ""; } /* otherwise, send to local subscribers */ sendit(notice, auth, who, external, external); } return(status); } void realm_init() { Client *client; Realm *rlm; int ii, jj; char rlmprinc[ANAME_SZ+INST_SZ+REALM_SZ+3]; nrealms = ngalaxies-1; otherrealms = (Realm *)malloc(nrealms * sizeof(Realm)); if (!otherrealms) { syslog(LOG_CRIT, "malloc failed in realm_init"); abort(); } /* copy necessary state from galaxy_list. */ rlm = otherrealms; for (ii = 0; ii < ngalaxies; ii++) { // skip the local galaxy if (strcmp(galaxy_list[ii].galaxy_config.galaxy, my_galaxy) == 0) continue; strcpy(rlm->name, galaxy_list[ii].galaxy_config.galaxy); rlm->count = galaxy_list[ii].galaxy_config.nservers; rlm->addrs = (struct sockaddr_in *)malloc(rlm->count * sizeof (struct sockaddr_in)); if (!rlm->addrs) { syslog(LOG_CRIT, "malloc failed in realm_init"); abort(); } for (jj = 0; jj < rlm->count; jj++) { rlm->addrs[jj].sin_family = AF_INET; /* use the server port */ rlm->addrs[jj].sin_port = srv_addr.sin_port; rlm->addrs[jj].sin_addr = galaxy_list[ii].galaxy_config.server_list[jj].addr; } client = (Client *) malloc(sizeof(Client)); if (!client) { syslog(LOG_CRIT, "malloc failed in realm_init"); abort(); } memset(&client->addr, 0, sizeof(struct sockaddr_in)); #ifdef HAVE_KRB4 memset(&client->session_key, 0, sizeof(client->session_key)); #endif sprintf(rlmprinc, "%s.%s@%s", SERVER_SERVICE, SERVER_INSTANCE, rlm->name); client->principal = make_string(rlmprinc, 0); client->last_send = 0; client->last_ack = NOW; client->subs = NULL; client->realm = rlm; client->addr.sin_family = 0; client->addr.sin_port = 0; client->addr.sin_addr.s_addr = 0; rlm->client = client; rlm->idx = (rlm->count) ? random() % rlm->count : 0; rlm->subs = NULL; rlm->remsubs = NULL; rlm->child_pid = 0; /* Assume the best */ rlm->state = REALM_TARDY; rlm->have_tkt = 1; rlm++; } } void realm_deathgram(server) Server *server; { Realm *realm; char rlm_recipient[REALM_SZ + 1]; int jj = 0; /* Get it out once, and assume foreign servers will share */ for (realm = otherrealms, jj = 0; jj < nrealms; jj++, realm++) { ZNotice_t snotice; char *pack; char rlm_recipient[REALM_SZ + 1]; int packlen, retval; memset (&snotice, 0, sizeof (snotice)); snotice.z_kind = ACKED; snotice.z_port = srv_addr.sin_port; snotice.z_class = ZEPHYR_CTL_CLASS; snotice.z_class_inst = ZEPHYR_CTL_REALM; snotice.z_opcode = SERVER_SHUTDOWN; snotice.z_sender = myname; /* my host name */ sprintf(rlm_recipient, "@%s", realm->name); snotice.z_recipient = rlm_recipient; snotice.z_default_format = ""; snotice.z_num_other_fields = 0; snotice.z_default_format = ""; snotice.z_message = (server) ? server->addr_str : NULL; snotice.z_message_len = (server) ? strlen(server->addr_str) + 1 : 0; zdbug((LOG_DEBUG, "rlm_deathgram: suggesting %s to %s", (server) ? server->addr_str : "nothing", realm->name)); #ifdef HAVE_KRB4 if (!ticket_lookup(realm->name)) if ((retval = ticket_retrieve(realm)) != ZERR_NONE) { syslog(LOG_WARNING, "rlm_deathgram failed: %s", error_message(retval)); return; } #endif if ((retval = ZFormatNotice(&snotice, &pack, &packlen, ZAUTH)) != ZERR_NONE) { syslog(LOG_WARNING, "rlm_deathgram format: %s", error_message(retval)); return; } if ((retval = ZParseNotice(pack, packlen, &snotice)) != ZERR_NONE) { syslog(LOG_WARNING, "rlm_deathgram parse: %s", error_message(retval)); free(pack); return; } realm_handoff(&snotice, 1, NULL, realm, 0); free(pack); } } void realm_wakeup() { int jj, found = 0; Realm *realm; char rlm_recipient[REALM_SZ + 1]; #ifdef HAVE_KRB4 char galaxy_config[1024]; Code_t code; /* XXX the library won't initialize from zhm, because there might not be one. We're about to call ZFormatNotice, which will call ZMakeAuthentication(), which will fail because there is no library galaxy list. So, we create a single galaxy entry with the local realm. */ __ngalaxies = 1; __galaxy_list = (Z_GalaxyList *) malloc(sizeof(Z_GalaxyList)*1); strcpy(galaxy_config, "local-galaxy hostlist localhost"); if ((code = Z_ParseGalaxyConfig(galaxy_config, &__galaxy_list[0].galaxy_config)) != ZERR_NONE) { syslog(LOG_ERR, "rlm_wakeup galaxy init: impossible error"); __ngalaxies = 0; free(__galaxy_list); return; } strcpy(__galaxy_list[0].krealm, my_krealm); __galaxy_list[0].last_authent_time = 0; #endif for (jj = 1; jj < nservers; jj++) { /* skip limbo server */ if (jj != me_server_idx && otherservers[jj].state == SERV_UP) found++; } if (nservers < 2 || !found) { /* if we're the only server up, send a REALM_BOOT to one of their servers here */ for (realm = otherrealms, jj = 0; jj < nrealms; jj++, realm++) { ZNotice_t snotice; char *pack; char rlm_recipient[REALM_SZ + 1]; int packlen, retval; memset (&snotice, 0, sizeof (snotice)); snotice.z_opcode = REALM_BOOT; snotice.z_port = srv_addr.sin_port; snotice.z_class_inst = ZEPHYR_CTL_REALM; snotice.z_class = ZEPHYR_CTL_CLASS; snotice.z_recipient = ""; snotice.z_kind = ACKED; snotice.z_num_other_fields = 0; snotice.z_default_format = ""; snotice.z_sender = myname; /* my host name */ sprintf(rlm_recipient, "@%s", realm->name); snotice.z_recipient = rlm_recipient; snotice.z_default_format = ""; snotice.z_message = NULL; snotice.z_message_len = 0; #ifdef HAVE_KRB4 if (!ticket_lookup(realm->name)) if ((retval = ticket_retrieve(realm)) != ZERR_NONE) { syslog(LOG_WARNING, "rlm_wakeup failed: %s", error_message(retval)); continue; } #endif if ((retval = ZFormatNotice(&snotice, &pack, &packlen, ZAUTH)) != ZERR_NONE) { syslog(LOG_WARNING, "rlm_wakeup format: %s", error_message(retval)); return; } if ((retval = ZParseNotice(pack, packlen, &snotice)) != ZERR_NONE) { syslog(LOG_WARNING, "rlm_wakeup parse: %s", error_message(retval)); free(pack); return; } realm_handoff(&snotice, 1, NULL, realm, 0); free(pack); } } } static Code_t realm_ulocate_dispatch(notice, auth, who, server, realm) ZNotice_t *notice; int auth; struct sockaddr_in *who; Server *server; Realm *realm; { register char *opcode = notice->z_opcode; Code_t status; if (!auth) { syslog(LOG_WARNING, "unauth locate msg from %s (%s/%s/%s)", inet_ntoa(who->sin_addr), notice->z_class, notice->z_class_inst, notice->z_opcode); /* XXX */ #if 0 syslog(LOG_WARNING, "unauth locate msg from %s", inet_ntoa(who->sin_addr)); #endif clt_ack(notice, who, AUTH_FAILED); return(ZERR_NONE); } if (!strcmp(opcode, REALM_REQ_LOCATE)) { ack(notice, who); ulogin_realm_locate(notice, who, realm); } else if (!strcmp(opcode, REALM_ANS_LOCATE)) { ack(notice, who); ulogin_relay_locate(notice, who); } else { syslog(LOG_WARNING, "%s unknown/illegal loc opcode %s", realm->name, opcode); nack(notice, who); } return(ZERR_NONE); } Code_t realm_control_dispatch(notice, auth, who, server, realm) ZNotice_t *notice; int auth; struct sockaddr_in *who; Server *server; Realm *realm; { register char *opcode = notice->z_opcode; Code_t status; if (!auth) { syslog(LOG_WARNING, "unauth ctl msg from %s (%s/%s/%s)", inet_ntoa(who->sin_addr), notice->z_class, notice->z_class_inst, notice->z_opcode); /* XXX */ #if 0 syslog(LOG_WARNING, "unauth ctl msg from %s", inet_ntoa(who->sin_addr)); #endif if (server == me_server) clt_ack(notice, who, AUTH_FAILED); return(ZERR_NONE); } if (strcmp(notice->z_class_inst, ZEPHYR_CTL_REALM)) { syslog(LOG_WARNING, "Invalid rlm_dispatch instance %s", notice->z_class_inst); return(ZERR_NONE); } if (!strcmp(opcode, REALM_REQ_SUBSCRIBE) || !strcmp(opcode, REALM_ADD_SUBSCRIBE)) { /* try to add subscriptions */ /* attempts to get defaults are ignored */ if ((status = subscr_foreign_user(notice, who, server, realm)) != ZERR_NONE) { clt_ack(notice, who, AUTH_FAILED); } else if (server == me_server) { server_forward(notice, auth, who); ack(notice, who); } } else if (!strcmp(opcode, REALM_UNSUBSCRIBE)) { /* try to remove subscriptions */ if ((status = subscr_realm_cancel(who, notice, realm)) != ZERR_NONE) { clt_ack(notice, who, NOT_FOUND); } else if (server == me_server) { server_forward(notice, auth, who); ack(notice, who); } } else if (!strcmp(opcode, REALM_BOOT)) { zdbug((LOG_DEBUG, "got a REALM_BOOT from %d (me %d)", server, me_server)); realm->state = REALM_STARTING; realm_set_server(who, realm); #ifdef REALM_MGMT /* resend subscriptions but only if this was to us */ if (server == me_server) { if ((status = subscr_realm_subs(realm)) != ZERR_NONE) { clt_ack(notice, who, NOT_FOUND); } else { /* do forward the hint in case it ever matters */ server_forward(notice, auth, who); ack(notice, who); } } #endif } else if (!strcmp(opcode, SERVER_SHUTDOWN)) { /* try to remove subscriptions */ if ((status = realm_new_server(who, notice, realm)) != ZERR_NONE) { clt_ack(notice, who, NOT_FOUND); } else if (server == me_server) { server_forward(notice, auth, who); ack(notice, who); } } else { syslog(LOG_WARNING, "%s unknown/illegal ctl opcode %s", realm->name, opcode); if (server == me_server) nack(notice, who); return(ZERR_NONE); } return(ZERR_NONE); } static Code_t realm_new_server(sin, notice, realm) struct sockaddr_in *sin; ZNotice_t *notice; Realm *realm; { struct hostent *hp; char suggested_server[MAXHOSTNAMELEN]; unsigned long addr; Realm *rlm; struct sockaddr_in sinaddr; int srvidx; if (!realm) return ZSRV_NORLM; srvidx = realm_get_idx_by_addr(realm, sin); zdbug((LOG_DEBUG, "rlm_new_srv: message from %d in %s (%s)", srvidx, realm->name, inet_ntoa(sin->sin_addr))); if (realm->idx == srvidx) { if (notice->z_message_len) { addr = inet_addr(notice->z_message); sinaddr.sin_addr.s_addr = addr; rlm = realm_which_realm(&sinaddr); /* Not exactly */ if (!rlm || (rlm != realm)) return ZSRV_NORLM; realm->idx = realm_get_idx_by_addr(realm, &sinaddr); } else { realm->idx = (realm->idx + 1) % realm->count; } zdbug((LOG_DEBUG, "rlm_new_srv: switched servers (%s)", inet_ntoa((realm->addrs[realm->idx]).sin_addr))); } else { zdbug((LOG_DEBUG, "rlm_new_srv: not switching servers (%s)", inet_ntoa((realm->addrs[realm->idx]).sin_addr))); } } static Code_t realm_set_server(sin, realm) struct sockaddr_in *sin; Realm *realm; { Realm *rlm; rlm = realm_which_realm(sin); /* Not exactly */ if (!rlm || (rlm != realm)) return ZSRV_NORLM; realm->idx = realm_get_idx_by_addr(realm, sin); zdbug((LOG_DEBUG, "rlm_pick_srv: switched servers (%s)", inet_ntoa((realm->addrs[realm->idx]).sin_addr))); } void realm_handoff(notice, auth, who, realm, ack_to_sender) ZNotice_t *notice; int auth; struct sockaddr_in *who; Realm *realm; int ack_to_sender; { #ifdef HAVE_KRB4 Code_t retval; if (!auth) { zdbug((LOG_DEBUG, "realm_sendit unauthentic to realm %s", realm->name)); realm_sendit(notice, who, auth, realm, ack_to_sender); return; } if (!ticket_lookup(realm->name)) if ((retval = ticket_retrieve(realm)) != ZERR_NONE) { syslog(LOG_WARNING, "rlm_handoff failed: %s", error_message(retval)); realm_sendit(notice, who, auth, realm, ack_to_sender); return; } zdbug((LOG_DEBUG, "realm_sendit to realm %s auth %d", realm->name, auth)); /* valid ticket available now, send the message */ realm_sendit_auth(notice, who, auth, realm, ack_to_sender); #else /* HAVE_KRB4 */ realm_sendit(notice, who, auth, realm, ack_to_sender); #endif /* HAVE_KRB4 */ } static void realm_sendit(notice, who, auth, realm, ack_to_sender) ZNotice_t *notice; struct sockaddr_in *who; int auth; Realm *realm; int ack_to_sender; { caddr_t pack; int packlen; Code_t retval; Unacked *nacked; notice->z_auth = auth; /* format the notice */ if ((retval = ZFormatRawNotice(notice, &pack, &packlen)) != ZERR_NONE) { syslog(LOG_WARNING, "rlm_sendit format: %s", error_message(retval)); return; } /* now send */ if ((retval = ZSetDestAddr(&realm->addrs[realm->idx])) != ZERR_NONE) { syslog(LOG_WARNING, "rlm_sendit set addr: %s", error_message(retval)); free(pack); return; } if ((retval = ZSendPacket(pack, packlen, 0)) != ZERR_NONE) { syslog(LOG_WARNING, "rlm_sendit xmit: %s", error_message(retval)); free(pack); return; } /* now we've sent it, mark it as not ack'ed */ if (!(nacked = (Unacked *)malloc(sizeof(Unacked)))) { /* no space: just punt */ syslog(LOG_ERR, "rlm_sendit nack malloc"); free(pack); return; } nacked->client = NULL; nacked->rexmits = 0; nacked->packet = pack; nacked->dest.rlm.rlm_idx = realm - otherrealms; nacked->dest.rlm.rlm_srv_idx = realm->idx; nacked->packsz = packlen; nacked->uid = notice->z_uid; if (ack_to_sender) nacked->ack_addr = *who; else nacked->ack_addr.sin_addr.s_addr = 0; /* set a timer to retransmit */ nacked->timer = timer_set_rel(rexmit_times[0], rlm_rexmit, nacked); /* chain in */ LIST_INSERT(&rlm_nacklist, nacked); return; } static void packet_ctl_nack(nackpacket) Unacked *nackpacket; { ZNotice_t notice; /* extract the notice */ ZParseNotice(nackpacket->packet, nackpacket->packsz, ¬ice); if (nackpacket->ack_addr.sin_addr.s_addr != 0) nack(¬ice, &nackpacket->ack_addr); #if 1 else syslog(LOG_WARNING, "would have acked nobody (%s/%s/%s)", notice.z_class, notice.z_class_inst, notice.z_opcode); /* XXX */ #endif } static void rlm_rexmit(arg) void *arg; { Unacked *nackpacket = (Unacked *) arg; Code_t retval; register Realm *realm; int new_srv_idx; zdbug((LOG_DEBUG,"rlm_rexmit")); realm = &otherrealms[nackpacket->dest.rlm.rlm_idx]; zdbug((LOG_DEBUG, "rlm_rexmit: sending to %s:%d (%d)", realm->name, realm->idx, nackpacket->rexmits)); if (realm->count == 0) return; /* Check to see if we've retransmitted as many times as we can */ if (nackpacket->rexmits >= (NUM_REXMIT_TIMES * realm->count)) { /* give a server ack that the packet is lost/realm dead */ packet_ctl_nack(nackpacket); LIST_DELETE(nackpacket); zdbug((LOG_DEBUG, "rlm_rexmit: %s appears dead", realm->name)); realm->state = REALM_DEAD; free(nackpacket->packet); free(nackpacket); return; } /* if we've reached our limit, move on to the next server */ if ((realm->state == REALM_TARDY) || (nackpacket->rexmits && !((nackpacket->rexmits+1) % (NUM_REXMIT_TIMES/3)))) { realm->idx = (realm->idx + 1) % realm->count; zdbug((LOG_DEBUG, "rlm_rexmit: %s switching servers:%d (%s)", realm->name, realm->idx, inet_ntoa((realm->addrs[realm->idx]).sin_addr))); } /* throttle back if it looks like the realm is down */ if ((realm->state != REALM_DEAD) || ((nackpacket->rexmits % (realm->count+1)) == 1)) { /* do the retransmit */ retval = ZSetDestAddr(&realm->addrs[realm->idx]); if (retval != ZERR_NONE) { syslog(LOG_WARNING, "rlm_rexmit set addr: %s", error_message(retval)); } else { retval = ZSendPacket(nackpacket->packet, nackpacket->packsz, 0); if (retval != ZERR_NONE) syslog(LOG_WARNING, "rlm_rexmit xmit: %s", error_message(retval)); } /* no per-server nack queues for foreign realms yet, doesn't matter */ nackpacket->dest.rlm.rlm_srv_idx = realm->idx; zdbug((LOG_DEBUG, "rlm_rexmit(%s): send to %s", realm->name, inet_ntoa((realm->addrs[realm->idx]).sin_addr))); } else { zdbug((LOG_DEBUG, "rlm_rexmit(%s): not sending to %s", realm->name, inet_ntoa((realm->addrs[realm->idx]).sin_addr))); } /* reset the timer */ nackpacket->rexmits++; nackpacket->timer = timer_set_rel(rexmit_times[nackpacket->rexmits%NUM_REXMIT_TIMES], rlm_rexmit, nackpacket); if (rexmit_times[nackpacket->rexmits%NUM_REXMIT_TIMES] == -1) zdbug((LOG_DEBUG, "rlm_rexmit(%s): would send at -1 to %s", realm->name, inet_ntoa((realm->addrs[realm->idx]).sin_addr))); return; } void realm_dump_realms(fp) FILE *fp; { register int ii, jj; for (ii = 0; ii < nrealms; ii++) { (void) fprintf(fp, "%d:%s\n", ii, otherrealms[ii].name); for (jj = 0; jj < otherrealms[ii].count; jj++) { (void) fprintf(fp, "\t%s\n", inet_ntoa(otherrealms[ii].addrs[jj].sin_addr)); } /* dump the subs */ subscr_dump_subs(fp, otherrealms[ii].subs); } } #ifdef HAVE_KRB4 static void realm_sendit_auth(notice, who, auth, realm, ack_to_sender) ZNotice_t *notice; int auth; struct sockaddr_in *who; Realm *realm; int ack_to_sender; { char *buffer, *ptr; caddr_t pack; int buffer_len, hdrlen, offset, fragsize, ret_len, message_len; int origoffset, origlen; Code_t retval; Unacked *nacked; char buf[1024], multi[64]; CREDENTIALS cred; KTEXT_ST authent; ZNotice_t partnotice, newnotice; struct in_addr my_addr; offset = 0; /* build an authent. first, make sure we have the ticket available */ retval = krb_get_cred(SERVER_SERVICE, SERVER_INSTANCE, realm->name, &cred); if (retval != GC_OK) { syslog(LOG_WARNING, "rlm_sendit_auth get_cred: %s", error_message(retval+krb_err_base)); return; } retval = krb_mk_req(&authent, SERVER_SERVICE, SERVER_INSTANCE, realm->name, 1); if (retval != MK_AP_OK) { syslog(LOG_WARNING, "rlm_sendit_auth mk_req: %s", error_message(retval+krb_err_base)); return; } retval = ZMakeAscii(buf, sizeof(buf), authent.dat, authent.length); if (retval != ZERR_NONE) { syslog(LOG_WARNING, "rlm_sendit_auth mk_ascii: %s", error_message(retval)); return; } /* set the dest addr */ retval = ZSetDestAddr(&realm->addrs[realm->idx]); if (retval != ZERR_NONE) { syslog(LOG_WARNING, "rlm_sendit_auth set addr: %s", error_message(retval)); return; } /* now format the notice, refragmenting if needed */ newnotice = *notice; newnotice.z_auth = 1; newnotice.z_ascii_authent = buf; newnotice.z_authent_len = authent.length; buffer = (char *) malloc(sizeof(ZPacket_t)); if (!buffer) { syslog(LOG_ERR, "realm_sendit_auth malloc"); return; /* DON'T put on nack list */ } buffer_len = sizeof(ZPacket_t); retval = Z_FormatRawHeader(&newnotice, buffer, buffer_len, &hdrlen, NULL, NULL, &ptr, NULL); if (retval != ZERR_NONE) { syslog(LOG_WARNING, "rlm_sendit_auth raw: %s", error_message(retval)); free(buffer); return; } #ifdef NOENCRYPTION newnotice.z_checksum = 0; #else newnotice.z_checksum = (ZChecksum_t)des_quad_cksum(buffer, NULL, ptr - buffer, 0, cred.session); #endif retval = Z_FormatRawHeader(&newnotice, buffer, buffer_len, &hdrlen, NULL, NULL, NULL, NULL); if (retval != ZERR_NONE) { syslog(LOG_WARNING, "rlm_sendit_auth raw: %s", error_message(retval)); free(buffer); return; } /* This is not terribly pretty, but it does do its job. * If a packet we get that needs to get sent off to another realm is * too big after we slap on our authent, we refragment it further, * a la Z_SendFragmentedNotice. This obviates the need for what * used to be done in ZFormatAuthenticRealmNotice, as we do it here. * At some point it should be pulled back out into its own function, * but only the server uses it. */ if ((newnotice.z_message_len+hdrlen > buffer_len) || (newnotice.z_message_len+hdrlen > Z_MAXPKTLEN)) { /* Deallocate buffer, use a local one */ free(buffer); partnotice = *notice; partnotice.z_auth = 1; partnotice.z_ascii_authent = buf; partnotice.z_authent_len = authent.length; origoffset = 0; origlen = notice->z_message_len; if (notice->z_multinotice && strcmp(notice->z_multinotice, "")) if (sscanf(notice->z_multinotice, "%d/%d", &origoffset, &origlen) != 2) { syslog(LOG_WARNING, "rlm_sendit_auth frag: parse failed"); return; } #if 0 zdbug((LOG_DEBUG,"rlm_send_auth: orig: %d-%d/%d", origoffset, notice->z_message_len, origlen)); #endif fragsize = Z_MAXPKTLEN-hdrlen-Z_FRAGFUDGE; Z_SourceAddr(&realm->addrs[realm->idx].sin_addr, &my_addr); while (offset < notice->z_message_len || !notice->z_message_len) { (void) sprintf(multi, "%d/%d", offset+origoffset, origlen); partnotice.z_multinotice = multi; if (offset > 0) { (void) gettimeofday(&partnotice.z_uid.tv, (struct timezone *)0); partnotice.z_uid.tv.tv_sec = htonl((u_long) partnotice.z_uid.tv.tv_sec); partnotice.z_uid.tv.tv_usec = htonl((u_long) partnotice.z_uid.tv.tv_usec); (void) memcpy((char *)&partnotice.z_uid.zuid_addr, &my_addr, sizeof(my_addr)); } message_len = min(notice->z_message_len-offset, fragsize); partnotice.z_message = notice->z_message+offset; partnotice.z_message_len = message_len; #if 0 zdbug((LOG_DEBUG,"rlm_send_auth: new: %d-%d/%d", origoffset+offset, message_len, origlen)); #endif buffer = (char *) malloc(sizeof(ZPacket_t)); if (!buffer) { syslog(LOG_ERR, "realm_sendit_auth malloc"); return; /* DON'T put on nack list */ } retval = Z_FormatRawHeader(&partnotice, buffer, buffer_len, &hdrlen, &ptr, NULL, NULL, NULL); if (retval != ZERR_NONE) { syslog(LOG_WARNING, "rlm_sendit_auth raw: %s", error_message(retval)); free(buffer); return; } #ifdef NOENCRYPTION partnotice.z_checksum = 0; #else partnotice.z_checksum = (ZChecksum_t)des_quad_cksum(buffer, NULL, ptr - buffer, 0, cred.session); #endif retval = Z_FormatRawHeader(&partnotice, buffer, buffer_len, &hdrlen, NULL, NULL, NULL, NULL); if (retval != ZERR_NONE) { syslog(LOG_WARNING, "rlm_sendit_auth raw: %s", error_message(retval)); free(buffer); return; } ptr = buffer+hdrlen; (void) memcpy(ptr, partnotice.z_message, partnotice.z_message_len); buffer_len = hdrlen+partnotice.z_message_len; /* now send */ if ((retval = ZSendPacket(buffer, buffer_len, 0)) != ZERR_NONE) { syslog(LOG_WARNING, "rlm_sendit_auth xmit: %s", error_message(retval)); free(buffer); return; } if (!(nacked = (Unacked *)malloc(sizeof(Unacked)))) { /* no space: just punt */ syslog(LOG_ERR, "rlm_sendit_auth nack malloc"); free(buffer); return; } nacked->rexmits = 0; nacked->packet = buffer; nacked->dest.rlm.rlm_idx = realm - otherrealms; nacked->dest.rlm.rlm_srv_idx = realm->idx; nacked->packsz = buffer_len; nacked->uid = partnotice.z_uid; /* Do the ack for the last frag, below */ if (ack_to_sender) nacked->ack_addr = *who; else nacked->ack_addr.sin_addr.s_addr = 0; /* set a timer to retransmit */ nacked->timer = timer_set_rel(rexmit_times[0], rlm_rexmit, nacked); /* chain in */ LIST_INSERT(&rlm_nacklist, nacked); offset += fragsize; if (!notice->z_message_len) break; } #if 0 zdbug((LOG_DEBUG, "rlm_sendit_auth frag message sent")); #endif } else { /* This is easy, no further fragmentation needed */ ptr = buffer+hdrlen; (void) memcpy(ptr, newnotice.z_message, newnotice.z_message_len); buffer_len = hdrlen+newnotice.z_message_len; /* now send */ if ((retval = ZSendPacket(buffer, buffer_len, 0)) != ZERR_NONE) { syslog(LOG_WARNING, "rlm_sendit_auth xmit: %s", error_message(retval)); free(buffer); return; } #if 0 zdbug((LOG_DEBUG, "rlm_sendit_auth message sent")); #endif /* now we've sent it, mark it as not ack'ed */ if (!(nacked = (Unacked *)malloc(sizeof(Unacked)))) { /* no space: just punt */ syslog(LOG_ERR, "rlm_sendit_auth nack malloc"); free(buffer); return; } nacked->rexmits = 0; nacked->packet = buffer; nacked->dest.rlm.rlm_idx = realm - otherrealms; nacked->dest.rlm.rlm_srv_idx = realm->idx; nacked->packsz = buffer_len; nacked->uid = notice->z_uid; /* Do the ack for the last frag, below */ if (ack_to_sender) nacked->ack_addr = *who; else nacked->ack_addr.sin_addr.s_addr = 0; /* set a timer to retransmit */ nacked->timer = timer_set_rel(rexmit_times[0], rlm_rexmit, nacked); /* chain in */ LIST_INSERT(&rlm_nacklist, nacked); } return; } static int ticket_expired(cred) CREDENTIALS *cred; { #ifdef HAVE_KRB_LIFE_TO_TIME return (krb_life_to_time(cred->issue_date, cred->lifetime) < NOW); #else /* HAVE_KRB_LIFE_TO_TIME */ return (cred->issue_date + cred->lifetime*5*60 < NOW); #endif /* HAVE_KRB_LIFE_TO_TIME */ } static int ticket_lookup(realm) char *realm; { CREDENTIALS cred; KTEXT_ST authent; int retval; retval = krb_get_cred(SERVER_SERVICE, SERVER_INSTANCE, realm, &cred); if (retval == GC_OK && !ticket_expired(&cred)) /* good ticket */ return(1); return (0); } static Code_t ticket_retrieve(realm) Realm *realm; { int pid, retval = 0; KTEXT_ST authent; get_tgt(); if (realm->child_pid) /* Right idea. Basically, we haven't gotten it yet */ return KRBET_KDC_AUTH_EXP; /* For Putrify */ memset(&authent.dat,0,MAX_KTXT_LEN); authent.mbz=0; if (realm->have_tkt) { retval = krb_mk_req(&authent, SERVER_SERVICE, SERVER_INSTANCE, realm->name, 0); if (retval == KSUCCESS) { return retval; } } else { syslog(LOG_ERR, "tkt_rtrv: don't have ticket, but have no child"); } pid = fork(); if (pid < 0) { syslog(LOG_ERR, "tkt_rtrv: can't fork"); return KRBET_KDC_AUTH_EXP; } else if (pid == 0) { #ifdef _POSIX_VERSION struct sigaction action; action.sa_flags = 0; sigemptyset(&action.sa_mask); action.sa_handler = 0; sigaction(SIGCHLD, &action, NULL); sigaction(SIGINT, &action, NULL); sigaction(SIGTERM, &action, NULL); sigaction(SIGUSR1, &action, NULL); sigaction(SIGUSR2, &action, NULL); sigaction(SIGFPE, &action, NULL); sigaction(SIGHUP, &action, NULL); #ifdef SIGEMT sigaction(SIGEMT, &action, NULL); #endif #else signal(SIGCHLD, SIG_DFL); signal(SIGINT, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGUSR1, SIG_DFL); signal(SIGUSR2, SIG_DFL); signal(SIGFPE, SIG_DFL); signal(SIGHUP, SIG_DFL); #ifdef SIGEMT signal(SIGEMT, SIG_DFL); #endif #endif while (1) { retval = krb_mk_req(&authent, SERVER_SERVICE, SERVER_INSTANCE, realm->name, 0); if (retval == KSUCCESS) exit(0); /* Sleep a little while before retrying */ sleep(30); } } else { realm->child_pid = pid; realm->have_tkt = 0; syslog(LOG_WARNING, "tkt_rtrv: %s: %s", realm->name, krb_err_txt[retval]); return (retval+krb_err_base); } } #endif /* HAVE_KRB4 */