diff options
Diffstat (limited to 'server/realm.c')
-rw-r--r-- | server/realm.c | 1086 |
1 files changed, 1086 insertions, 0 deletions
diff --git a/server/realm.c b/server/realm.c new file mode 100644 index 0000000..de2dc12 --- /dev/null +++ b/server/realm.c @@ -0,0 +1,1086 @@ +#include "zserver.h" +#include <sys/socket.h> + +extern int __My_length; +extern char *__My_addr; + +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 */ + +static void get_realm_addrs __P(()); +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)); +#ifdef ZEPHYR_USES_KERBEROS +static Code_t ticket_retrieve __P((Realm *realm)); +#endif + +char * +realm_expand_realm(realmname) +char *realmname; +{ + static char expand[REALM_SZ]; + static char krb_realm[REALM_SZ+1]; + char *cp1, *cp2; + int retval; + FILE *rlm_file; + char list_file[128]; + char linebuf[BUFSIZ]; + char scratch[128]; + + /* upcase what we got */ + cp2 = realmname; + cp1 = expand; + while (*cp2) { + *cp1++ = toupper(*cp2++); + } + *cp1 = '\0'; + + sprintf(list_file, "%s/%s", CONFDIR, REALM_LIST_FILE); + + if ((rlm_file = fopen(list_file, "r")) == (FILE *) 0) { + return(expand); + } + + if (fgets(linebuf, BUFSIZ, rlm_file) == NULL) { + /* error reading */ + (void) fclose(rlm_file); + return(expand); + } + + while (1) { + /* run through the file, looking for admin host */ + if (fgets(linebuf, BUFSIZ, rlm_file) == NULL) { + (void) fclose(rlm_file); + return(expand); + } + + if (sscanf(linebuf, "%s %s", krb_realm, scratch) < 2) + continue; + if (!strncmp(krb_realm, expand, strlen(expand))) { + (void) fclose(rlm_file); + return(krb_realm); + } + } +#ifdef KERBEROS + if (!strncmp(my_realm, expand, strlen(expand))) + return(my_realm); +#endif + return(expand); +} + +Realmname * +get_realm_lists(file) + char *file; +{ + Realmname *rlm_list, *rlm; + int ii, nused, ntotal; + FILE *fp; + char buf[REALM_SZ + MAXHOSTNAMELEN + 1]; /* one for newline */ + char realm[REALM_SZ], server[MAXHOSTNAMELEN + 1]; + + nused = 0; + if (!(fp = fopen(file, "r"))) + return((Realmname *)0); + + /* start with 16, realloc if necessary */ + ntotal = 16; + rlm_list = (Realmname *)malloc(ntotal * sizeof(Realmname)); + if (!rlm_list) { + syslog(LOG_CRIT, "get_realm_lists malloc"); + abort(); + } + + while (fgets(buf, REALM_SZ + MAXHOSTNAMELEN + 1, fp)) { + if (sscanf(buf, "%s %s", realm, server) != 2) { + syslog(LOG_CRIT, "bad format in %s", file); + abort(); + } + for (ii = 0; ii < nused; ii++) { + /* look for this realm */ + if (!strcmp(rlm_list[ii].name, realm)) + break; + } + if (ii < nused) { + rlm = &rlm_list[ii]; + if (rlm->nused +1 >= rlm->nservers) { + /* make more space */ + rlm->servers = (char **)realloc((char *)rlm->servers, + (unsigned)rlm->nservers * 2 * + sizeof(char *)); + if (!rlm->servers) { + syslog(LOG_CRIT, "get_realm_lists realloc"); + abort(); + } + rlm->nservers *= 2; + } + rlm->servers[rlm->nused++] = strsave(server); + } else { + /* new realm */ + if (nused + 1 >= ntotal) { + /* make more space */ + rlm_list = (Realmname *)realloc((char *)rlm_list, + (unsigned)ntotal * 2 * + sizeof(Realmname)); + if (!rlm_list) { + syslog(LOG_CRIT, "get_realm_lists realloc"); + abort(); + } + ntotal *= 2; + } + rlm = &rlm_list[nused++]; + strcpy(rlm->name, realm); + rlm->nused = 0; + rlm->nservers = 16; + rlm->servers = (char **)malloc(rlm->nservers * sizeof(char *)); + if (!rlm->servers) { + syslog(LOG_CRIT, "get_realm_lists malloc"); + abort(); + } + rlm->servers[rlm->nused++] = strsave(server); + } + } + if (nused + 1 >= ntotal) { + rlm_list = (Realmname *)realloc((char *)rlm_list, + (unsigned)(ntotal + 1) * + sizeof(Realmname)); + if (!rlm_list) { + syslog(LOG_CRIT, "get_realm_lists realloc"); + abort(); + } + } + *rlm_list[nused].name = '\0'; + + return(rlm_list); +} + +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 +bound_for_local_realm(notice) + ZNotice_t *notice; +{ + char *realm; + + realm = strchr(notice->z_recipient, '@'); + + if (!realm || !strcmp(realm_expand_realm(realm + 1), ZGetRealm())) + return 1; + + return 0; +} + +int +sender_in_realm(notice) + ZNotice_t *notice; +{ + char *realm; + + realm = strchr(notice->z_sender, '@'); + + if (!realm || !strcmp(realm + 1, ZGetRealm())) + return 1; + + return 0; +} + +Realm * +realm_which_realm(who) + struct sockaddr_in *who; +{ + Realm *realm; + struct sockaddr_in *addr; + int a, b; + + /* 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; + + for (realm = otherrealms, a = 0; a < nrealms; a++, realm++) + if (!strcmp(realm->name, 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; + ZNotice_t acknotice; + ZPacket_t retval; + +#if 0 + 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) { + 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); + } + } 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; + /* only send to our realm */ + external = 0; + } else if (bound_for_local_realm(notice) && *notice->z_recipient + == '@') + { + /* we're responsible for getting this message out */ + external = 1; + notice->z_recipient = ""; + } + + /* otherwise, send to local subscribers */ + sendit(notice, auth, who, external); + } + + return(status); +} + +void +realm_init() +{ + Client *client; + Realmname *rlmnames; + Realm *rlm; + int ii, jj, found; + struct in_addr *addresses; + struct hostent *hp; + char list_file[128]; + char rlmprinc[ANAME_SZ+INST_SZ+REALM_SZ+3]; + + sprintf(list_file, "%s/%s", CONFDIR, REALM_LIST_FILE); + rlmnames = get_realm_lists(list_file); + if (!rlmnames) { + zdbug((LOG_DEBUG, "No other realms")); + nrealms = 0; + return; + } + + for (nrealms = 0; *rlmnames[nrealms].name; nrealms++); + + otherrealms = (Realm *)malloc(nrealms * sizeof(Realm)); + if (!otherrealms) { + syslog(LOG_CRIT, "malloc failed in get_realm_addrs"); + abort(); + } + + for (ii = 0; ii < nrealms; ii++) { + rlm = &otherrealms[ii]; + strcpy(rlm->name, rlmnames[ii].name); + + addresses = (struct in_addr *)malloc(rlmnames[ii].nused * sizeof(struct in_addr)); + if (!addresses) { + syslog(LOG_CRIT, "malloc failed in get_realm_addrs"); + abort(); + } + /* convert names to addresses */ + found = 0; + for (jj = 0; jj < rlmnames[ii].nused; jj++) { + hp = gethostbyname(rlmnames[ii].servers[jj]); + if (hp) { + memmove((caddr_t) &addresses[found], (caddr_t)hp->h_addr, sizeof(struct in_addr)); + found++; + } else + syslog(LOG_WARNING, "hostname failed, %s", rlmnames[ii].servers[jj]); + /* free the hostname */ + free(rlmnames[ii].servers[jj]); + } + rlm->count = found; + rlm->addrs = (struct sockaddr_in *)malloc(found * sizeof (struct sockaddr_in)); + if (!rlm->addrs) { + syslog(LOG_CRIT, "malloc failed in get_realm_addrs"); + 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 = addresses[jj]; + } + client = (Client *) malloc(sizeof(Client)); + if (!client) { + syslog(LOG_CRIT, "malloc failed in get_realm_addrs"); + abort(); + } + memset(&client->addr, 0, sizeof(struct sockaddr_in)); +#ifdef ZEPHYR_USES_KERBEROS + 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_msg = 0; + client->last_check = 0; + client->last_send = 0; + 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 = random() % rlm->count; + rlm->subs = NULL; + rlm->tkt_try = 0; + free(rlmnames[ii].servers); + free(addresses); + } + free(rlmnames); +} + +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", + inet_ntoa(who->sin_addr)); + 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", + inet_ntoa(who->sin_addr)); + 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, 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 { + 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); +} + +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 ZEPHYR_USES_KERBEROS + 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); + } + + if (!ticket_lookup(realm->name)) + if ((retval = ticket_retrieve(realm)) != ZERR_NONE) { + syslog(LOG_WARNING, "rlm_handoff failed: %s", error_message(retval)); + 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 /* ZEPHYR_USES_KERBEROS */ + realm_sendit(notice, who, auth, realm, ack_to_sender); +#endif /* ZEPHYR_USES_KERBEROS */ +} + +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->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); + nack(¬ice, &nackpacket->ack_addr); +} + +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", realm->name)); + + if (rexmit_times[(nackpacket->rexmits + 1)/(realm->count)] == -1) { + /* give a server ack that the packet is lost/realm dead */ + packet_ctl_nack(nackpacket); + LIST_DELETE(nackpacket); + free(nackpacket->packet); + free(nackpacket); + + zdbug((LOG_DEBUG, "rlm_rexmit: %s appears dead", realm->name)); + return; + } + + /* retransmit the packet, trying each server in the realm multiple times */ +#if 0 + new_srv_idx = ((nackpacket->rexmits / NUM_REXMIT_TIMES) + + nackpacket->rlm.rlm_srv_idx) % realm->count; +#else + new_srv_idx = nackpacket->rexmits % realm->count; +#endif + if (new_srv_idx != realm->idx) + realm->idx = new_srv_idx; + + 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)); + } + /* reset the timer */ + if (rexmit_times[(nackpacket->rexmits + 1)/(realm->count)] != -1) + nackpacket->rexmits++; + + nackpacket->timer = timer_set_rel(rexmit_times[(nackpacket->rexmits)/(realm->count)], rlm_rexmit, nackpacket); + 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 ZEPHYR_USES_KERBEROS +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; + + offset = 0; + + /* first, build an authent */ + retval = krb_get_cred(SERVER_SERVICE, SERVER_INSTANCE, realm, &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, 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, &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); + 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 obliviates 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; + + 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); + if ((retval = Z_GetMyAddr()) != ZERR_NONE) { + syslog(LOG_WARNING, "rlm_sendit_auth addr: %s", error_message(retval)); + return; + } + (void) memcpy((char *)&partnotice.z_uid.zuid_addr, __My_addr, + __My_length); + } + 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); + 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); + 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; + } + + offset += fragsize; + + 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); + + 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); + } +#if 0 + if (ack_to_sender) + nacked->ack_addr = *who; +#endif + return; +} + +int +ticket_expired(cred) +CREDENTIALS *cred; +{ + /* extra 15 minutes for safety margin */ +#ifdef AFS_LIFETIMES + return (krb_life_to_time(cred->issue_date, cred->lifetime) < NOW + 15*60); +#else /* AFS_LIFETIMES */ + return (cred->issue_date + cred->lifetime*5*60 < NOW + 15*60); +#endif /* AFS_LIFETIMES */ +} + +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); + + if (!strcmp(realm, ZGetRealm())) { + get_tgt(); + + /* For Putrify */ + memset(&authent.dat,0,MAX_KTXT_LEN); + authent.mbz=0; + + /* this is local, so try to contact the Kerberos server */ + retval = krb_mk_req(&authent, SERVER_SERVICE, SERVER_INSTANCE, + realm, 0); + if (retval != KSUCCESS) { + syslog(LOG_ERR, "tkt_lookup: local: %s", + krb_err_txt[retval]); + return(0); + } else { + return(1); + } + } + + return (0); +} + +static Code_t +ticket_retrieve(realm) + Realm *realm; +{ + int pid, retval; + KTEXT_ST authent; + + get_tgt(); + + /* For Putrify */ + memset(&authent.dat,0,MAX_KTXT_LEN); + authent.mbz=0; + + /* Don't lose by trying too often. */ + if (NOW - realm->tkt_try > 5 * 60) { + retval = krb_mk_req(&authent, SERVER_SERVICE, SERVER_INSTANCE, + realm->name, 0); + realm->tkt_try = NOW; + if (retval != KSUCCESS) { + syslog(LOG_WARNING, "tkt_rtrv: %s: %s", realm, + krb_err_txt[retval]); + return (retval+krb_err_base); + } + return (0); + } else { + return (1); + } +} +#endif /* ZEPHYR_USES_KERBEROS */ + |