1665 lines
45 KiB
C
1665 lines
45 KiB
C
#include "zserver.h"
|
|
#include <sys/socket.h>
|
|
|
|
Unacked *rlm_nacklist = NULL; /* not acked list for realm-realm
|
|
packets */
|
|
ZRealm **otherrealms = NULL; /* points to an array of the known
|
|
servers */
|
|
int nrealms = 0; /* number of other realms */
|
|
int n_realm_slots = 0; /* size of malloc'd otherrealms */
|
|
|
|
/*
|
|
* External Routines:
|
|
*
|
|
* ZRealm *realm_which_realm(struct sockaddr_in *who)
|
|
* figures out if this packet came from another realm's server
|
|
*
|
|
* ZRealm *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_bound_for_realm(char *realm, char *recip)
|
|
* figures out if recip is in realm, expanding recip's realm
|
|
*
|
|
* int realm_sender_in_realm(char *realm, char *sender)
|
|
* figures out if sender is in realm
|
|
*
|
|
* ZRealm *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
|
|
*
|
|
* Code_t realm_control_dispatch(ZNotice_t *notice, int auth,
|
|
* struct sockaddr_in *who, Server *server,
|
|
* ZRealm *realm)
|
|
* dispatches a foreign realm control message
|
|
*
|
|
* void realm_handoff(ZNotice_t *notice, int auth, struct sockaddr_in *who,
|
|
* ZRealm *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 int realm_next_idx_by_idx(ZRealm *realm, int idx);
|
|
static void realm_sendit(ZNotice_t *notice, struct sockaddr_in *who, int auth, ZRealm *realm, int ack_to_sender);
|
|
#ifdef HAVE_KRB5
|
|
static Code_t realm_sendit_auth(ZNotice_t *notice, struct sockaddr_in *who, int auth, ZRealm *realm, int ack_to_sender);
|
|
#endif
|
|
static void rlm_ack(ZNotice_t *notice, Unacked *nacked);
|
|
static void rlm_nack_cancel(ZNotice_t *notice, struct sockaddr_in *who);
|
|
static void rlm_rexmit(void *arg);
|
|
static Code_t realm_ulocate_dispatch(ZNotice_t *notice,int auth,struct sockaddr_in *who,Server *server,ZRealm *realm);
|
|
static Code_t realm_new_server(struct sockaddr_in *, ZNotice_t *, ZRealm *);
|
|
static Code_t realm_set_server(struct sockaddr_in *, ZRealm *);
|
|
#ifdef HAVE_KRB5
|
|
static Code_t ticket_retrieve(ZRealm *realm);
|
|
static int ticket_lookup(char *realm);
|
|
#endif
|
|
|
|
static int
|
|
is_usable(ZRealm_server *srvr)
|
|
{
|
|
return !srvr->deleted && srvr->got_addr;
|
|
}
|
|
|
|
static int
|
|
is_sendable(ZRealm_server *srvr)
|
|
{
|
|
return !srvr->deleted && srvr->got_addr && !srvr->dontsend;
|
|
}
|
|
|
|
static void
|
|
rlm_wakeup_cb(void *arg)
|
|
{
|
|
ZRealm *realm = arg;
|
|
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_KRB5
|
|
if (!ticket_lookup(realm->name))
|
|
if ((retval = ticket_retrieve(realm)) != ZERR_NONE) {
|
|
syslog(LOG_WARNING, "rlm_wakeup failed: %s",
|
|
error_message(retval));
|
|
return;
|
|
}
|
|
#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 void
|
|
rlm_set_server_address(ZRealm_server *srvr, struct hostent *hp)
|
|
{
|
|
memmove(&srvr->addr.sin_addr, hp->h_addr, sizeof(struct in_addr));
|
|
/* use the server port */
|
|
srvr->addr.sin_port = srv_addr.sin_port;
|
|
srvr->addr.sin_family = AF_INET;
|
|
srvr->got_addr = 1;
|
|
if (is_sendable(srvr) && srvr->realm->state == REALM_NEW) {
|
|
srvr->realm->idx = realm_next_idx_by_idx(srvr->realm, srvr->realm->idx);
|
|
srvr->realm->state = REALM_TARDY;
|
|
/*
|
|
* Set a timer to send a wakeup to this realm. We do this rather
|
|
* than just sending the notice now because, if we are not using
|
|
* C-ARES, then we might be called during server startup before
|
|
* the server is prepared to send notices.
|
|
*/
|
|
timer_set_rel(0, rlm_wakeup_cb, srvr->realm);
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_ARES
|
|
|
|
static void rlm_server_address_timer_cb(void *srvr);
|
|
static void rlm_server_address_lookup_cb(void *, int, int, struct hostent *);
|
|
|
|
static void
|
|
rlm_lookup_server_address(ZRealm_server *srvr)
|
|
{
|
|
/* Cancel any pending future lookup. */
|
|
if (srvr->timer) {
|
|
timer_reset(srvr->timer);
|
|
srvr->timer = NULL;
|
|
}
|
|
ares_gethostbyname(achannel, srvr->name->string, AF_INET,
|
|
rlm_server_address_lookup_cb, srvr);
|
|
}
|
|
|
|
static void
|
|
rlm_server_address_timer_cb(void *arg)
|
|
{
|
|
ZRealm_server *srvr = arg;
|
|
|
|
srvr->timer = NULL;
|
|
ares_gethostbyname(achannel, srvr->name->string, AF_INET,
|
|
rlm_server_address_lookup_cb, arg);
|
|
}
|
|
|
|
static void
|
|
rlm_server_address_lookup_cb(void *arg, int status, int timeouts,
|
|
struct hostent *hp)
|
|
{
|
|
ZRealm_server *srvr = arg;
|
|
int delay = 30;
|
|
|
|
if (status == ARES_SUCCESS) {
|
|
rlm_set_server_address(srvr, hp);
|
|
delay = 24 * 3600; /* Check again once per day */
|
|
} else {
|
|
syslog(LOG_WARNING, "%s: hostname lookup failed: %s",
|
|
srvr->name->string, ares_strerror(status));
|
|
}
|
|
|
|
/*
|
|
* Set a timer to trigger another lookup.
|
|
* But, not if the server is deleted, which may have happened
|
|
* while we were waiting for ARES to finish the last lookup.
|
|
* Also, not if there is already a timer, which is possible if
|
|
* there were two outstanding lookups and we are the second to
|
|
* complete. This can happen if we are asked to refresh the
|
|
* server list while a previous lookup is still in progress,
|
|
* since there is no convenient way to tell whether there is a
|
|
* lookup in progress.
|
|
*/
|
|
if (!srvr->timer && !srvr->deleted)
|
|
srvr->timer = timer_set_rel(delay, rlm_server_address_timer_cb, arg);
|
|
}
|
|
|
|
#else
|
|
|
|
static void
|
|
rlm_lookup_server_address(ZRealm_server *srvr)
|
|
{
|
|
struct hostent *hp;
|
|
|
|
hp = gethostbyname(srvr->name->string);
|
|
if (hp)
|
|
rlm_set_server_address(srvr, hp);
|
|
else
|
|
syslog(LOG_WARNING, "hostname failed, %s", srvr->name->string);
|
|
}
|
|
|
|
#endif
|
|
|
|
static int
|
|
realm_get_idx_by_addr(ZRealm *realm,
|
|
struct sockaddr_in *who)
|
|
{
|
|
ZRealm_server *srvr;
|
|
int b;
|
|
|
|
/* loop through the realms */
|
|
for (b = 0; b < realm->count; b++) {
|
|
srvr = realm->srvrs[b];
|
|
if (!is_usable(srvr))
|
|
continue;
|
|
if (srvr->addr.sin_addr.s_addr == who->sin_addr.s_addr)
|
|
return(b);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
realm_next_idx_by_idx(ZRealm *realm, int idx)
|
|
{
|
|
ZRealm_server *srvr;
|
|
int b;
|
|
|
|
/* loop through the servers */
|
|
for (b = idx; b < realm->count; b++) {
|
|
srvr = realm->srvrs[b];
|
|
if (is_sendable(srvr))
|
|
return(b);
|
|
}
|
|
|
|
/* recycle */
|
|
if (idx != 0)
|
|
for (b = 0; b < idx; b++) {
|
|
srvr = realm->srvrs[b];
|
|
if (is_sendable(srvr))
|
|
return(b);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
const char *
|
|
realm_expand_realm(char *realmname)
|
|
{
|
|
int a;
|
|
|
|
/* First, look for an exact match (case insensitive) */
|
|
#if defined(HAVE_KRB4) || defined(HAVE_KRB5)
|
|
if (!strcasecmp(ZGetRealm(), realmname))
|
|
return(ZGetRealm());
|
|
#endif
|
|
|
|
for (a = 0; a < nrealms; a++)
|
|
if (!strcasecmp(otherrealms[a]->name, realmname))
|
|
return(otherrealms[a]->name);
|
|
|
|
/* No exact match. See if there's a partial match */
|
|
#if defined(HAVE_KRB4) || defined(HAVE_KRB5)
|
|
if (!strncasecmp(ZGetRealm(), realmname, strlen(realmname)))
|
|
return(ZGetRealm());
|
|
#endif
|
|
|
|
for (a = 0; a < nrealms; a++)
|
|
if (!strncasecmp(otherrealms[a]->name, realmname, strlen(realmname)))
|
|
return(otherrealms[a]->name);
|
|
return(realmname);
|
|
}
|
|
|
|
ZRealm *
|
|
realm_get_realm_by_pid(int pid)
|
|
{
|
|
int a;
|
|
|
|
for (a = 0; a < nrealms; a++)
|
|
if (otherrealms[a]->child_pid == pid)
|
|
return(otherrealms[a]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
kill_realm_pids(void)
|
|
{
|
|
int a;
|
|
|
|
for (a = 0; a < nrealms; a++)
|
|
if (otherrealms[a]->child_pid != 0)
|
|
kill(otherrealms[a]->child_pid, 9);
|
|
|
|
return;
|
|
}
|
|
|
|
static ZRealmname *
|
|
get_realm_lists(char *file)
|
|
{
|
|
ZRealmname *rlm_list, *rlm;
|
|
int ii, nused, ntotal;
|
|
FILE *fp;
|
|
char buf[REALM_SZ + NS_MAXDNAME + 1]; /* one for newline */
|
|
char realm[REALM_SZ], server[NS_MAXDNAME + 1];
|
|
String *realm_name;
|
|
|
|
nused = 0;
|
|
if (!(fp = fopen(file, "r")))
|
|
return((ZRealmname *)0);
|
|
|
|
/* start with 16, realloc if necessary */
|
|
ntotal = 16;
|
|
rlm_list = (ZRealmname *)malloc(ntotal * sizeof(ZRealmname));
|
|
if (!rlm_list) {
|
|
syslog(LOG_CRIT, "get_realm_lists malloc");
|
|
abort();
|
|
}
|
|
|
|
while (fgets(buf, sizeof(buf), fp)) {
|
|
if (sscanf(buf, "%s %s", realm, server) != 2) {
|
|
syslog(LOG_CRIT, "bad format in %s", file);
|
|
abort();
|
|
}
|
|
realm_name = make_string(realm, 0);
|
|
for (ii = 0; ii < nused; ii++) {
|
|
/* look for this realm */
|
|
if (rlm_list[ii].name == realm_name)
|
|
break;
|
|
}
|
|
if (ii < nused) {
|
|
free_string(realm_name);
|
|
rlm = &rlm_list[ii];
|
|
if (rlm->nused +1 >= rlm->nservers) {
|
|
/* make more space */
|
|
rlm->servers = (struct _ZRealm_server *)
|
|
realloc((char *)rlm->servers,
|
|
(unsigned)rlm->nservers * 2 *
|
|
sizeof(struct _ZRealm_server));
|
|
if (!rlm->servers) {
|
|
syslog(LOG_CRIT, "get_realm_lists realloc");
|
|
abort();
|
|
}
|
|
rlm->nservers *= 2;
|
|
}
|
|
} else {
|
|
/* new realm */
|
|
if (nused + 1 >= ntotal) {
|
|
/* make more space */
|
|
rlm_list = (ZRealmname *)realloc((char *)rlm_list,
|
|
(unsigned)ntotal * 2 *
|
|
sizeof(ZRealmname));
|
|
if (!rlm_list) {
|
|
syslog(LOG_CRIT, "get_realm_lists realloc");
|
|
abort();
|
|
}
|
|
ntotal *= 2;
|
|
}
|
|
rlm = &rlm_list[nused++];
|
|
rlm->name = realm_name;
|
|
rlm->nused = 0;
|
|
rlm->nservers = 16;
|
|
rlm->servers = (struct _ZRealm_server *)
|
|
malloc(rlm->nservers * sizeof(struct _ZRealm_server));
|
|
if (!rlm->servers) {
|
|
syslog(LOG_CRIT, "get_realm_lists malloc");
|
|
abort();
|
|
}
|
|
}
|
|
memset(&rlm->servers[rlm->nused], 0, sizeof(struct _ZRealm_server));
|
|
if (*server == '/') {
|
|
rlm->servers[rlm->nused].name = make_string(server + 1, 1);
|
|
rlm->servers[rlm->nused].dontsend = 1;
|
|
} else {
|
|
rlm->servers[rlm->nused].name = make_string(server, 1);
|
|
}
|
|
rlm->nused++;
|
|
}
|
|
if (nused + 1 >= ntotal) {
|
|
rlm_list = (ZRealmname *)realloc((char *)rlm_list,
|
|
(unsigned)(ntotal + 1) *
|
|
sizeof(ZRealmname));
|
|
if (!rlm_list) {
|
|
syslog(LOG_CRIT, "get_realm_lists realloc");
|
|
abort();
|
|
}
|
|
}
|
|
rlm_list[nused].name = 0;
|
|
|
|
fclose(fp);
|
|
return(rlm_list);
|
|
}
|
|
|
|
Code_t
|
|
realm_send_realms(void)
|
|
{
|
|
int cnt, retval;
|
|
for (cnt = 0; cnt < nrealms; cnt++) {
|
|
retval = subscr_send_realm_subs(otherrealms[cnt]);
|
|
if (retval != ZERR_NONE)
|
|
return(retval);
|
|
}
|
|
return ZERR_NONE;
|
|
}
|
|
|
|
int
|
|
realm_bound_for_realm(const char *realm, char *recip)
|
|
{
|
|
char *rlm = NULL;
|
|
int remote = strcmp(ZGetRealm(), realm);
|
|
|
|
if (recip)
|
|
rlm = strchr(recip, '@');
|
|
|
|
if (!rlm && !remote)
|
|
return 1;
|
|
|
|
if (rlm && strcmp(realm_expand_realm(rlm + 1), realm) == 0)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
realm_sender_in_realm(const char *realm, char *sender)
|
|
{
|
|
char *rlm = NULL;
|
|
int remote = strcmp(ZGetRealm(), realm);
|
|
|
|
if (sender)
|
|
rlm = strchr(sender, '@');
|
|
|
|
if (!rlm && !remote)
|
|
return 1;
|
|
|
|
if (rlm && strcmp((rlm + 1), realm) == 0)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
ZRealm *
|
|
realm_which_realm(struct sockaddr_in *who)
|
|
{
|
|
ZRealm_server *srvr;
|
|
int a, b;
|
|
|
|
if (who->sin_port != srv_addr.sin_port)
|
|
return 0;
|
|
|
|
/* loop through the realms */
|
|
for (a = 0; a < nrealms; a++)
|
|
/* loop through the addresses for the realm */
|
|
for (b = 0; b < otherrealms[a]->count; b++) {
|
|
srvr = otherrealms[a]->srvrs[b];
|
|
if (!is_usable(srvr))
|
|
continue;
|
|
if (srvr->addr.sin_addr.s_addr == who->sin_addr.s_addr)
|
|
return(otherrealms[a]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
ZRealm *
|
|
realm_get_realm_by_name(char *name)
|
|
{
|
|
int a;
|
|
|
|
/* First, look for an exact match (case insensitive) */
|
|
for (a = 0; a < nrealms; a++)
|
|
if (!strcasecmp(otherrealms[a]->name, name))
|
|
return(otherrealms[a]);
|
|
|
|
/* Failing that, look for an inexact match */
|
|
for (a = 0; a < nrealms; a++)
|
|
if (!strncasecmp(otherrealms[a]->name, name, strlen(name)))
|
|
return(otherrealms[a]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
ZRealm *
|
|
realm_get_realm_by_name_string(String *namestr)
|
|
{
|
|
int a;
|
|
|
|
for (a = 0; a < nrealms; a++)
|
|
if (otherrealms[a]->namestr == namestr)
|
|
return otherrealms[a];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
rlm_nack_cancel(register ZNotice_t *notice,
|
|
struct sockaddr_in *who)
|
|
{
|
|
register ZRealm *which = realm_which_realm(who);
|
|
register Unacked *nacked;
|
|
|
|
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));
|
|
|
|
if (!which) {
|
|
syslog(LOG_ERR, "non-realm ack?");
|
|
return;
|
|
}
|
|
|
|
for (nacked = rlm_nacklist; nacked; nacked = nacked->next) {
|
|
if (nacked->dest.rlm.realm == 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);
|
|
Unacked_delete(nacked);
|
|
free(nacked);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rlm_ack(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(ZNotice_t *notice,
|
|
int auth,
|
|
struct sockaddr_in *who,
|
|
Server *server)
|
|
{
|
|
ZRealm *realm;
|
|
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);
|
|
}
|
|
|
|
/* 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;
|
|
external = 0;
|
|
} else if (realm_bound_for_realm(ZGetRealm(), notice->z_recipient)
|
|
&& *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(void)
|
|
{
|
|
Client *client;
|
|
ZRealmname *rlmnames;
|
|
ZRealm *rlm;
|
|
int ii, jj, kk, nrlmnames, nsendable;
|
|
char realm_list_file[128];
|
|
char rlmprinc[MAX_PRINCIPAL_SIZE];
|
|
|
|
sprintf(realm_list_file, "%s/zephyr/%s", SYSCONFDIR, REALM_LIST_FILE);
|
|
rlmnames = get_realm_lists(realm_list_file);
|
|
if (!rlmnames) {
|
|
zdbug((LOG_DEBUG, "No other realms"));
|
|
/* should we nuke all existing server records? */
|
|
return;
|
|
}
|
|
|
|
for (nrlmnames = 0; rlmnames[nrlmnames].name; nrlmnames++);
|
|
|
|
/*
|
|
* This happens only when we first start up. Otherwise, otherrealms
|
|
* is grown as needed.
|
|
*/
|
|
if (!otherrealms) {
|
|
otherrealms = (ZRealm **)malloc(nrlmnames * sizeof(ZRealm *));
|
|
if (!otherrealms) {
|
|
syslog(LOG_CRIT, "malloc failed in realm_init");
|
|
abort();
|
|
}
|
|
memset(otherrealms, 0, (nrlmnames * sizeof(ZRealm *)));
|
|
n_realm_slots = nrlmnames;
|
|
}
|
|
|
|
/* ii: entry in rlmnames */
|
|
for (ii = 0; ii < nrlmnames; ii++) {
|
|
nsendable = 0;
|
|
rlm = realm_get_realm_by_name_string(rlmnames[ii].name);
|
|
if (rlm) {
|
|
/* jj: server entry in otherrealms */
|
|
/* kk: server entry in rlmnames */
|
|
for (jj = 0; jj < rlm->count; jj++) {
|
|
rlm->srvrs[jj]->deleted = 1;
|
|
for (kk = 0; kk < rlmnames[ii].nused; kk++) {
|
|
if (rlmnames[ii].servers[kk].name != rlm->srvrs[jj]->name)
|
|
continue;
|
|
/* update existing server */
|
|
rlm->srvrs[jj]->dontsend = rlmnames[ii].servers[kk].dontsend;
|
|
rlm->srvrs[jj]->deleted = 0;
|
|
rlm_lookup_server_address(rlm->srvrs[jj]);
|
|
if (is_sendable(rlm->srvrs[jj])) nsendable++;
|
|
|
|
/* mark realm.list server entry used */
|
|
rlmnames[ii].servers[kk].deleted = 1;
|
|
break;
|
|
}
|
|
if (rlm->srvrs[jj]->deleted && rlm->srvrs[jj]->timer) {
|
|
timer_reset(rlm->srvrs[jj]->timer);
|
|
rlm->srvrs[jj]->timer = NULL;
|
|
}
|
|
}
|
|
for (jj = kk = 0; kk < rlmnames[ii].nused; kk++)
|
|
if (!rlmnames[ii].servers[kk].deleted) jj++;
|
|
|
|
rlm->srvrs = realloc(rlm->srvrs,
|
|
(rlm->count + jj) * sizeof(ZRealm_server *));
|
|
if (!rlm->srvrs) {
|
|
syslog(LOG_CRIT, "realloc failed in realm_init");
|
|
abort();
|
|
}
|
|
for (kk = 0; kk < rlmnames[ii].nused; kk++) {
|
|
if (rlmnames[ii].servers[kk].deleted) continue;
|
|
rlm->srvrs[rlm->count] = malloc(sizeof(ZRealm_server));
|
|
if (!rlm->srvrs[rlm->count]) {
|
|
syslog(LOG_CRIT, "realloc failed in realm_init");
|
|
abort();
|
|
}
|
|
*(rlm->srvrs[rlm->count]) = rlmnames[ii].servers[kk];
|
|
rlm->srvrs[rlm->count]->realm = rlm;
|
|
rlm_lookup_server_address(rlm->srvrs[rlm->count]);
|
|
if (is_sendable(rlm->srvrs[rlm->count])) nsendable++;
|
|
rlm->count++;
|
|
}
|
|
/* The current server might have been deleted or marked dontsend.
|
|
Advance to one we can use, if necessary. */
|
|
if (nsendable) {
|
|
rlm->idx = realm_next_idx_by_idx(rlm, rlm->idx);
|
|
} else {
|
|
rlm->idx = 0;
|
|
rlm->state = REALM_NEW;
|
|
}
|
|
free(rlmnames[ii].servers);
|
|
continue;
|
|
}
|
|
|
|
if (nrealms >= n_realm_slots) {
|
|
otherrealms = realloc(otherrealms,
|
|
n_realm_slots * 2 * sizeof(ZRealm *));
|
|
if (!otherrealms) {
|
|
syslog(LOG_CRIT, "realloc failed in realm_init");
|
|
abort();
|
|
}
|
|
memset(otherrealms + n_realm_slots, 0,
|
|
n_realm_slots * sizeof(ZRealm *));
|
|
n_realm_slots *= 2;
|
|
}
|
|
|
|
rlm = (ZRealm *) malloc(sizeof(ZRealm));
|
|
if (!rlm) {
|
|
syslog(LOG_CRIT, "malloc failed in realm_init");
|
|
abort();
|
|
}
|
|
memset(rlm, 0, sizeof(ZRealm));
|
|
otherrealms[nrealms++] = rlm;
|
|
|
|
rlm->namestr = rlmnames[ii].name;
|
|
rlm->name = rlm->namestr->string;
|
|
rlm->state = REALM_NEW;
|
|
|
|
/* convert names to addresses */
|
|
rlm->count = rlmnames[ii].nused;
|
|
rlm->srvrs = malloc(rlm->count * sizeof(ZRealm_server *));
|
|
if (!rlm->srvrs) {
|
|
syslog(LOG_CRIT, "malloc failed in realm_init");
|
|
abort();
|
|
}
|
|
for (jj = 0; jj < rlm->count; jj++) {
|
|
rlm->srvrs[jj] = &rlmnames[ii].servers[jj];
|
|
rlm->srvrs[jj]->realm = rlm;
|
|
rlm_lookup_server_address(rlm->srvrs[jj]);
|
|
if (is_sendable(rlm->srvrs[jj])) nsendable++;
|
|
}
|
|
|
|
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_KRB5
|
|
client->session_keyblock = NULL;
|
|
#else
|
|
#ifdef HAVE_KRB4
|
|
memset(&client->session_key, 0, sizeof(client->session_key));
|
|
#endif
|
|
#endif
|
|
snprintf(rlmprinc, MAX_PRINCIPAL_SIZE, "%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 = (nsendable) ?
|
|
realm_next_idx_by_idx(rlm, (random() % rlm->count)) : 0;
|
|
rlm->subs = NULL;
|
|
rlm->remsubs = NULL;
|
|
rlm->child_pid = 0;
|
|
rlm->have_tkt = 1;
|
|
}
|
|
free(rlmnames);
|
|
}
|
|
|
|
void
|
|
realm_deathgram(Server *server)
|
|
{
|
|
ZRealm *realm;
|
|
int jj = 0;
|
|
|
|
/* Get it out once, and assume foreign servers will share */
|
|
for (jj = 0; jj < nrealms; jj++) {
|
|
ZNotice_t snotice;
|
|
char *pack;
|
|
char rlm_recipient[REALM_SZ + 1];
|
|
int packlen, retval;
|
|
|
|
realm = otherrealms[jj];
|
|
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_KRB5
|
|
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, ZCAUTH))
|
|
!= 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);
|
|
}
|
|
}
|
|
|
|
static Code_t
|
|
realm_ulocate_dispatch(ZNotice_t *notice,
|
|
int auth,
|
|
struct sockaddr_in *who,
|
|
Server *server,
|
|
ZRealm *realm)
|
|
{
|
|
register char *opcode = notice->z_opcode;
|
|
|
|
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 */
|
|
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(ZNotice_t *notice,
|
|
int auth,
|
|
struct sockaddr_in *who,
|
|
Server *server,
|
|
ZRealm *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 (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 %s",
|
|
inet_ntoa(server->addr.sin_addr)));
|
|
if (realm->state != REALM_UP) 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(struct sockaddr_in *sin,
|
|
ZNotice_t *notice,
|
|
ZRealm *realm)
|
|
{
|
|
unsigned long addr;
|
|
ZRealm *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;
|
|
/* Validate the hint */
|
|
realm->idx =
|
|
realm_next_idx_by_idx(realm, realm_get_idx_by_addr(realm,
|
|
&sinaddr));
|
|
} else {
|
|
realm->idx = realm_next_idx_by_idx(realm, (realm->idx + 1) %
|
|
realm->count);
|
|
}
|
|
zdbug((LOG_DEBUG, "rlm_new_srv: switched servers (%s)", inet_ntoa((realm->srvrs[realm->idx]->addr).sin_addr)));
|
|
} else {
|
|
zdbug((LOG_DEBUG, "rlm_new_srv: not switching servers (%s)", inet_ntoa((realm->srvrs[realm->idx]->addr).sin_addr)));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static Code_t
|
|
realm_set_server(struct sockaddr_in *sin,
|
|
ZRealm *realm)
|
|
{
|
|
ZRealm *rlm;
|
|
int idx;
|
|
|
|
rlm = realm_which_realm(sin);
|
|
/* Not exactly */
|
|
if (!rlm || (rlm != realm))
|
|
return ZSRV_NORLM;
|
|
idx = realm_get_idx_by_addr(realm, sin);
|
|
|
|
/* Not exactly */
|
|
if (!is_sendable(realm->srvrs[idx]))
|
|
return ZSRV_NORLM;
|
|
|
|
realm->idx = idx;
|
|
|
|
zdbug((LOG_DEBUG, "rlm_pick_srv: switched servers (%s)", inet_ntoa((realm->srvrs[realm->idx]->addr).sin_addr)));
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
realm_handoff(ZNotice_t *notice,
|
|
int auth,
|
|
struct sockaddr_in *who,
|
|
ZRealm *realm,
|
|
int ack_to_sender)
|
|
{
|
|
#ifdef HAVE_KRB5
|
|
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 */
|
|
retval = 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(ZNotice_t *notice,
|
|
struct sockaddr_in *who,
|
|
int auth,
|
|
ZRealm *realm,
|
|
int ack_to_sender)
|
|
{
|
|
char *pack;
|
|
int packlen;
|
|
Code_t retval;
|
|
Unacked *nacked;
|
|
|
|
if (realm->count == 0 || realm->state == REALM_NEW) {
|
|
/* XXX we should have a queue or something */
|
|
syslog(LOG_WARNING, "rlm_sendit no servers for %s", realm->name);
|
|
return;
|
|
}
|
|
|
|
notice->z_auth = auth;
|
|
notice->z_authent_len = 0;
|
|
notice->z_ascii_authent = "";
|
|
notice->z_checksum = 0;
|
|
|
|
/* 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->srvrs[realm->idx]->addr)) != 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;
|
|
}
|
|
|
|
memset(nacked, 0, sizeof(Unacked));
|
|
nacked->packet = pack;
|
|
nacked->dest.rlm.realm = realm;
|
|
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 */
|
|
Unacked_insert(&rlm_nacklist, nacked);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
packet_ctl_nack(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);
|
|
else
|
|
syslog(LOG_WARNING, "would have acked nobody (%s/%s/%s)",
|
|
notice.z_class, notice.z_class_inst, notice.z_opcode); /* XXX */
|
|
}
|
|
|
|
static void
|
|
rlm_rexmit(void *arg)
|
|
{
|
|
Unacked *nackpacket = (Unacked *) arg;
|
|
Code_t retval;
|
|
register ZRealm *realm;
|
|
|
|
zdbug((LOG_DEBUG,"rlm_rexmit"));
|
|
|
|
realm = nackpacket->dest.rlm.realm;
|
|
|
|
zdbug((LOG_DEBUG, "rlm_rexmit: sending to %s:%d (%d)",
|
|
realm->name, realm->idx, nackpacket->rexmits));
|
|
|
|
if (realm->count == 0 || realm->state == REALM_NEW)
|
|
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);
|
|
Unacked_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_next_idx_by_idx(realm, (realm->idx + 1) %
|
|
realm->count);
|
|
zdbug((LOG_DEBUG, "rlm_rexmit: %s switching servers:%d (%s)",
|
|
realm->name, realm->idx,
|
|
inet_ntoa((realm->srvrs[realm->idx]->addr).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->srvrs[realm->idx]->addr);
|
|
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->srvrs[realm->idx]->addr).sin_addr)));
|
|
} else {
|
|
zdbug((LOG_DEBUG, "rlm_rexmit(%s): not sending to %s", realm->name,
|
|
inet_ntoa((realm->srvrs[realm->idx]->addr).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->srvrs[realm->idx]->addr).sin_addr)));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
realm_dump_realms(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%s%s%s\n",
|
|
inet_ntoa(otherrealms[ii]->srvrs[jj]->addr.sin_addr),
|
|
otherrealms[ii]->srvrs[jj]->dontsend ? " nosend" : "",
|
|
otherrealms[ii]->srvrs[jj]->got_addr ? " gotaddr" : "",
|
|
otherrealms[ii]->srvrs[jj]->deleted ? " deleted" : "");
|
|
}
|
|
/* dump the subs */
|
|
subscr_dump_subs(fp, otherrealms[ii]->subs);
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_KRB5
|
|
|
|
static Code_t
|
|
realm_auth_sendit_nacked(char *buffer, int packlen, ZRealm *realm,
|
|
ZUnique_Id_t uid, int ack_to_sender,
|
|
struct sockaddr_in *who)
|
|
{
|
|
Unacked *nacked;
|
|
|
|
nacked = (Unacked *) malloc(sizeof(Unacked));
|
|
if (nacked == NULL)
|
|
return ENOMEM;
|
|
|
|
memset(nacked, 0, sizeof(Unacked));
|
|
nacked->packet = buffer;
|
|
nacked->dest.rlm.realm = realm;
|
|
nacked->dest.rlm.rlm_srv_idx = realm->idx;
|
|
nacked->packsz = packlen;
|
|
nacked->uid = 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 */
|
|
Unacked_insert(&rlm_nacklist, nacked);
|
|
|
|
return ZERR_NONE;
|
|
}
|
|
|
|
static Code_t
|
|
realm_sendit_auth(ZNotice_t *notice,
|
|
struct sockaddr_in *who,
|
|
int auth,
|
|
ZRealm *realm,
|
|
int ack_to_sender)
|
|
{
|
|
char *buffer = NULL;
|
|
int hdrlen, offset, fragsize, message_len;
|
|
int origoffset, origlen;
|
|
Code_t retval;
|
|
char multi[64];
|
|
ZNotice_t partnotice, newnotice;
|
|
|
|
if (realm->count == 0 || realm->state == REALM_NEW) {
|
|
/* XXX we should have a queue or something */
|
|
syslog(LOG_WARNING, "rlm_sendit_auth no servers for %s", realm->name);
|
|
return ZERR_INTERNAL;
|
|
}
|
|
|
|
offset = 0;
|
|
|
|
buffer = (char *)malloc(sizeof(ZPacket_t));
|
|
if (!buffer) {
|
|
syslog(LOG_ERR, "realm_sendit_auth malloc");
|
|
return ENOMEM; /* DON'T put on nack list */
|
|
}
|
|
|
|
newnotice = *notice;
|
|
|
|
hdrlen = 0;
|
|
retval = ZMakeZcodeRealmAuthentication(&newnotice, buffer, sizeof(ZPacket_t),
|
|
&hdrlen, realm->name);
|
|
if (retval)
|
|
syslog(LOG_WARNING,
|
|
"rlm_sendit_auth: ZMakeZcodeRealmAuthentication: %s",
|
|
error_message(retval));
|
|
|
|
if (!retval) {
|
|
retval = ZSetDestAddr(&realm->srvrs[realm->idx]->addr);
|
|
if (retval)
|
|
syslog(LOG_WARNING, "rlm_sendit_auth: ZSetDestAddr: %s",
|
|
error_message(retval));
|
|
}
|
|
|
|
/* 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 (!retval &&
|
|
((notice->z_message_len+hdrlen > (int)sizeof(ZPacket_t)) ||
|
|
(notice->z_message_len+hdrlen > Z_MAXPKTLEN))) {
|
|
|
|
/* Reallocate buffers inside the refragmenter */
|
|
free(buffer);
|
|
buffer = NULL;
|
|
|
|
partnotice = *notice;
|
|
|
|
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: multinotice parse failed");
|
|
retval = ZERR_BADFIELD;
|
|
}
|
|
}
|
|
|
|
fragsize = Z_MAXPKTLEN - hdrlen - Z_FRAGFUDGE;
|
|
|
|
if (fragsize < 0)
|
|
retval = ZERR_HEADERLEN;
|
|
|
|
while (!retval &&
|
|
(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)Z_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));
|
|
partnotice.z_sender_sockaddr.ip4.sin_family = AF_INET; /* XXX */
|
|
(void)memcpy((char *)&partnotice.z_sender_sockaddr.ip4.sin_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;
|
|
|
|
buffer = (char *)malloc(sizeof(ZPacket_t));
|
|
if (!buffer) {
|
|
syslog(LOG_ERR, "realm_sendit_auth malloc");
|
|
retval = ENOMEM; /* DON'T put on nack list */
|
|
}
|
|
|
|
if (!retval) {
|
|
retval = ZMakeZcodeRealmAuthentication(&partnotice, buffer,
|
|
sizeof(ZPacket_t),
|
|
&hdrlen,
|
|
realm->name);
|
|
if (retval != ZERR_NONE)
|
|
syslog(LOG_WARNING, "rlm_sendit_auth set addr: %s",
|
|
error_message(retval));
|
|
}
|
|
|
|
if (!retval) {
|
|
(void) memcpy(buffer + hdrlen, partnotice.z_message,
|
|
partnotice.z_message_len);
|
|
|
|
retval = ZSendPacket(buffer,
|
|
hdrlen + partnotice.z_message_len, 0);
|
|
if (retval)
|
|
syslog(LOG_WARNING, "rlm_sendit_auth xmit: %s",
|
|
error_message(retval));
|
|
}
|
|
|
|
if (!retval) {
|
|
retval = realm_auth_sendit_nacked(buffer, hdrlen +
|
|
partnotice.z_message_len,realm,
|
|
partnotice.z_uid,
|
|
ack_to_sender, who);
|
|
if (retval) /* no space: just punt */
|
|
syslog(LOG_ERR,
|
|
"rlm_sendit_auth: realm_auth_sendit_nacked: %s",
|
|
error_message(retval));
|
|
}
|
|
|
|
if (!retval)
|
|
offset += fragsize;
|
|
|
|
if (!notice->z_message_len)
|
|
break;
|
|
}
|
|
} else if (!retval) {
|
|
/* This is easy, no further fragmentation needed */
|
|
(void)memcpy(buffer + hdrlen, newnotice.z_message,
|
|
newnotice.z_message_len);
|
|
|
|
retval = ZSendPacket(buffer, hdrlen + newnotice.z_message_len, 0);
|
|
if (retval)
|
|
syslog(LOG_WARNING, "rlm_sendit_auth xmit: %s",
|
|
error_message(retval));
|
|
else {
|
|
retval = realm_auth_sendit_nacked(buffer,
|
|
hdrlen + newnotice.z_message_len,
|
|
realm, newnotice.z_uid,
|
|
ack_to_sender, who);
|
|
if (retval) /* no space: just punt */
|
|
syslog(LOG_ERR, "rlm_sendit_auth: realm_auth_sendit_nacked: %s",
|
|
error_message(retval));
|
|
}
|
|
}
|
|
|
|
if (retval && buffer != NULL)
|
|
free(buffer);
|
|
return retval;
|
|
}
|
|
|
|
static int
|
|
ticket_lookup(char *realm)
|
|
{
|
|
krb5_error_code result;
|
|
krb5_timestamp sec;
|
|
krb5_ccache ccache;
|
|
krb5_creds creds_in, creds;
|
|
|
|
result = krb5_cc_default(Z_krb5_ctx, &ccache);
|
|
if (result)
|
|
return 0;
|
|
|
|
memset(&creds_in, 0, sizeof(creds_in));
|
|
memset(&creds, 0, sizeof(creds));
|
|
|
|
result = krb5_cc_get_principal(Z_krb5_ctx, ccache, &creds_in.client);
|
|
if (result) {
|
|
krb5_cc_close(Z_krb5_ctx, ccache);
|
|
return 0;
|
|
}
|
|
|
|
result = krb5_build_principal(Z_krb5_ctx, &creds_in.server,
|
|
strlen(realm),
|
|
realm,
|
|
SERVER_KRB5_SERVICE, SERVER_INSTANCE,
|
|
NULL);
|
|
if (result) {
|
|
krb5_cc_close(Z_krb5_ctx, ccache);
|
|
return 0;
|
|
}
|
|
|
|
result = krb5_cc_retrieve_cred(Z_krb5_ctx, ccache, 0, &creds_in, &creds);
|
|
krb5_cc_close(Z_krb5_ctx, ccache);
|
|
/* good ticket? */
|
|
|
|
krb5_timeofday (Z_krb5_ctx, &sec);
|
|
krb5_free_cred_contents(Z_krb5_ctx, &creds_in); /* hope this is OK */
|
|
if ((result == 0) && (sec < creds.times.endtime)) {
|
|
krb5_free_cred_contents(Z_krb5_ctx, &creds);
|
|
return (1);
|
|
}
|
|
if (!result)
|
|
krb5_free_cred_contents(Z_krb5_ctx, &creds);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static Code_t
|
|
ticket_retrieve(ZRealm *realm)
|
|
{
|
|
int pid;
|
|
krb5_ccache ccache;
|
|
krb5_error_code result;
|
|
krb5_creds creds_in, *creds;
|
|
|
|
get_tgt();
|
|
|
|
if (realm->child_pid)
|
|
/* Right idea. Basically, we haven't gotten it yet */
|
|
return KRB5KRB_AP_ERR_TKT_EXPIRED;
|
|
|
|
if (realm->have_tkt) {
|
|
/* Get a pointer to the default ccache. We don't need to free this. */
|
|
result = krb5_cc_default(Z_krb5_ctx, &ccache);
|
|
|
|
/* GRRR. There's no allocator or constructor for krb5_creds */
|
|
/* GRRR. It would be nice if this API were documented at all */
|
|
memset(&creds_in, 0, sizeof(creds_in));
|
|
|
|
if (!result)
|
|
result = krb5_cc_get_principal(Z_krb5_ctx, ccache, &creds_in.client);
|
|
/* construct the service principal */
|
|
if (!result)
|
|
result = krb5_build_principal(Z_krb5_ctx, &creds_in.server,
|
|
strlen(realm->name), realm->name,
|
|
SERVER_KRB5_SERVICE, SERVER_INSTANCE,
|
|
NULL);
|
|
|
|
/* HOLDING: creds_in.server */
|
|
|
|
/* look up or get the credentials we need */
|
|
if (!result)
|
|
result = krb5_get_credentials(Z_krb5_ctx, 0 /* flags */, ccache,
|
|
&creds_in, &creds);
|
|
krb5_cc_close(Z_krb5_ctx, ccache);
|
|
krb5_free_cred_contents(Z_krb5_ctx, &creds_in); /* hope this is OK */
|
|
if (!result) {
|
|
krb5_free_creds(Z_krb5_ctx, creds);
|
|
return 0;
|
|
}
|
|
} else {
|
|
syslog(LOG_ERR, "tkt_rtrv: don't have ticket, but have no child");
|
|
result = KRB5KRB_AP_ERR_TKT_EXPIRED;
|
|
}
|
|
|
|
pid = fork();
|
|
if (pid < 0) {
|
|
syslog(LOG_ERR, "tkt_rtrv: can't fork");
|
|
return errno;
|
|
}
|
|
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
|
|
|
|
syslog(LOG_INFO, "tkt_rtrv running for %s", realm->name);
|
|
while (1) {
|
|
/* Get a pointer to the default ccache.
|
|
We don't need to free this. */
|
|
result = krb5_cc_default(Z_krb5_ctx, &ccache);
|
|
|
|
/* GRRR. There's no allocator or constructor for krb5_creds */
|
|
/* GRRR. It would be nice if this API were documented at all */
|
|
memset(&creds_in, 0, sizeof(creds_in));
|
|
|
|
if (!result)
|
|
result = krb5_cc_get_principal(Z_krb5_ctx, ccache,
|
|
&creds_in.client);
|
|
/* construct the service principal */
|
|
if (!result)
|
|
result = krb5_build_principal(Z_krb5_ctx, &creds_in.server,
|
|
strlen(realm->name), realm->name,
|
|
SERVER_KRB5_SERVICE,
|
|
SERVER_INSTANCE,
|
|
NULL);
|
|
|
|
/* HOLDING: creds_in.server */
|
|
|
|
/* look up or get the credentials we need */
|
|
if (!result)
|
|
result = krb5_get_credentials(Z_krb5_ctx, 0 /* flags */, ccache,
|
|
&creds_in, &creds);
|
|
krb5_cc_close(Z_krb5_ctx, ccache);
|
|
krb5_free_cred_contents(Z_krb5_ctx, &creds_in); /* hope this is OK */
|
|
if (!result) {
|
|
krb5_free_creds(Z_krb5_ctx, creds);
|
|
syslog(LOG_INFO, "tkt_rtrv succeeded for %s", realm->name);
|
|
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: %d", realm->name,
|
|
result);
|
|
return (result);
|
|
}
|
|
}
|
|
#endif /* HAVE_KRB5 */
|