diff options
Diffstat (limited to 'crypto/heimdal/lib/kadm5/ipropd_master.c')
-rw-r--r-- | crypto/heimdal/lib/kadm5/ipropd_master.c | 429 |
1 files changed, 364 insertions, 65 deletions
diff --git a/crypto/heimdal/lib/kadm5/ipropd_master.c b/crypto/heimdal/lib/kadm5/ipropd_master.c index 537d403..bd8f71f 100644 --- a/crypto/heimdal/lib/kadm5/ipropd_master.c +++ b/crypto/heimdal/lib/kadm5/ipropd_master.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan + * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -34,24 +34,34 @@ #include "iprop.h" #include <rtbl.h> -RCSID("$Id: ipropd_master.c,v 1.29 2003/03/19 11:56:38 lha Exp $"); +RCSID("$Id: ipropd_master.c 22211 2007-12-07 19:27:27Z lha $"); static krb5_log_facility *log_facility; -const char *slave_stats_file = KADM5_SLAVE_STATS; +const char *slave_stats_file; +const char *slave_time_missing = "2 min"; +const char *slave_time_gone = "5 min"; + +static int time_before_missing; +static int time_before_gone; + +const char *master_hostname; static int make_signal_socket (krb5_context context) { struct sockaddr_un addr; + const char *fn; int fd; + fn = kadm5_log_signal_socket(context); + fd = socket (AF_UNIX, SOCK_DGRAM, 0); if (fd < 0) krb5_err (context, 1, errno, "socket AF_UNIX"); memset (&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - strlcpy (addr.sun_path, KADM5_LOG_SIGNAL, sizeof(addr.sun_path)); + strlcpy (addr.sun_path, fn, sizeof(addr.sun_path)); unlink (addr.sun_path); if (bind (fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) krb5_err (context, 1, errno, "bind %s", addr.sun_path); @@ -59,7 +69,7 @@ make_signal_socket (krb5_context context) } static int -make_listen_socket (krb5_context context) +make_listen_socket (krb5_context context, const char *port_str) { int fd; int one = 1; @@ -71,8 +81,24 @@ make_listen_socket (krb5_context context) setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)); memset (&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; - addr.sin_port = krb5_getportbyname (context, - IPROP_SERVICE, "tcp", IPROP_PORT); + + if (port_str) { + addr.sin_port = krb5_getportbyname (context, + port_str, "tcp", + 0); + if (addr.sin_port == 0) { + char *ptr; + long port; + + port = strtol (port_str, &ptr, 10); + if (port == 0 && ptr == port_str) + krb5_errx (context, 1, "bad port `%s'", port_str); + addr.sin_port = htons(port); + } + } else { + addr.sin_port = krb5_getportbyname (context, IPROP_SERVICE, + "tcp", IPROP_PORT); + } if(bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) krb5_err (context, 1, errno, "bind"); if (listen(fd, SOMAXCONN) < 0) @@ -85,10 +111,11 @@ struct slave { struct sockaddr_in addr; char *name; krb5_auth_context ac; - u_int32_t version; + uint32_t version; time_t seen; unsigned long flags; #define SLAVE_F_DEAD 0x1 +#define SLAVE_F_AYT 0x2 struct slave *next; }; @@ -97,16 +124,27 @@ typedef struct slave slave; static int check_acl (krb5_context context, const char *name) { + const char *fn; FILE *fp; char buf[256]; int ret = 1; + char *slavefile; + + asprintf(&slavefile, "%s/slaves", hdb_db_dir(context)); - fp = fopen (KADM5_SLAVE_ACL, "r"); + fn = krb5_config_get_string_default(context, + NULL, + slavefile, + "kdc", + "iprop-acl", + NULL); + + fp = fopen (fn, "r"); + free(slavefile); if (fp == NULL) return 1; while (fgets(buf, sizeof(buf), fp) != NULL) { - if (buf[strlen(buf) - 1 ] == '\n') - buf[strlen(buf) - 1 ] = '\0'; + buf[strcspn(buf, "\r\n")] = '\0'; if (strcmp (buf, name) == 0) { ret = 0; break; @@ -119,12 +157,31 @@ check_acl (krb5_context context, const char *name) static void slave_seen(slave *s) { + s->flags &= ~SLAVE_F_AYT; s->seen = time(NULL); } +static int +slave_missing_p (slave *s) +{ + if (time(NULL) > s->seen + time_before_missing) + return 1; + return 0; +} + +static int +slave_gone_p (slave *s) +{ + if (time(NULL) > s->seen + time_before_gone) + return 1; + return 0; +} + static void -slave_dead(slave *s) +slave_dead(krb5_context context, slave *s) { + krb5_warnx(context, "slave %s dead", s->name); + if (s->fd >= 0) { close (s->fd); s->fd = -1; @@ -177,7 +234,11 @@ add_slave (krb5_context context, krb5_keytab keytab, slave **root, int fd) krb5_warn (context, errno, "accept"); goto error; } - gethostname(hostname, sizeof(hostname)); + if (master_hostname) + strlcpy(hostname, master_hostname, sizeof(hostname)); + else + gethostname(hostname, sizeof(hostname)); + ret = krb5_sname_to_principal (context, hostname, IPROP_NAME, KRB5_NT_SRV_HST, &server); if (ret) { @@ -240,13 +301,14 @@ struct prop_context { }; static int -prop_one (krb5_context context, HDB *db, hdb_entry *entry, void *v) +prop_one (krb5_context context, HDB *db, hdb_entry_ex *entry, void *v) { krb5_error_code ret; + krb5_storage *sp; krb5_data data; - struct slave *slave = (struct slave *)v; + struct slave *s = (struct slave *)v; - ret = hdb_entry2value (context, entry, &data); + ret = hdb_entry2value (context, &entry->entry, &data); if (ret) return ret; ret = krb5_data_realloc (&data, data.length + 4); @@ -255,18 +317,25 @@ prop_one (krb5_context context, HDB *db, hdb_entry *entry, void *v) return ret; } memmove ((char *)data.data + 4, data.data, data.length - 4); - _krb5_put_int (data.data, ONE_PRINC, 4); + sp = krb5_storage_from_data(&data); + if (sp == NULL) { + krb5_data_free (&data); + return ENOMEM; + } + krb5_store_int32(sp, ONE_PRINC); + krb5_storage_free(sp); - ret = krb5_write_priv_message (context, slave->ac, &slave->fd, &data); + ret = krb5_write_priv_message (context, s->ac, &s->fd, &data); krb5_data_free (&data); return ret; } static int send_complete (krb5_context context, slave *s, - const char *database, u_int32_t current_version) + const char *database, uint32_t current_version) { krb5_error_code ret; + krb5_storage *sp; HDB *db; krb5_data data; char buf[8]; @@ -274,11 +343,15 @@ send_complete (krb5_context context, slave *s, ret = hdb_create (context, &db, database); if (ret) krb5_err (context, 1, ret, "hdb_create: %s", database); - ret = db->open (context, db, O_RDONLY, 0); + ret = db->hdb_open (context, db, O_RDONLY, 0); if (ret) krb5_err (context, 1, ret, "db->open"); - _krb5_put_int(buf, TELL_YOU_EVERYTHING, 4); + sp = krb5_storage_from_mem (buf, 4); + if (sp == NULL) + krb5_errx (context, 1, "krb5_storage_from_mem"); + krb5_store_int32 (sp, TELL_YOU_EVERYTHING); + krb5_storage_free (sp); data.data = buf; data.length = 4; @@ -287,26 +360,34 @@ send_complete (krb5_context context, slave *s, if (ret) { krb5_warn (context, ret, "krb5_write_priv_message"); - slave_dead(s); + slave_dead(context, s); return ret; } ret = hdb_foreach (context, db, 0, prop_one, s); if (ret) { krb5_warn (context, ret, "hdb_foreach"); - slave_dead(s); + slave_dead(context, s); return ret; } - _krb5_put_int (buf, NOW_YOU_HAVE, 4); - _krb5_put_int (buf + 4, current_version, 4); + (*db->hdb_close)(context, db); + (*db->hdb_destroy)(context, db); + + sp = krb5_storage_from_mem (buf, 8); + if (sp == NULL) + krb5_errx (context, 1, "krb5_storage_from_mem"); + krb5_store_int32 (sp, NOW_YOU_HAVE); + krb5_store_int32 (sp, current_version); + krb5_storage_free (sp); + data.length = 8; s->version = current_version; ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); if (ret) { - slave_dead(s); + slave_dead(context, s); krb5_warn (context, ret, "krb5_write_priv_message"); return ret; } @@ -317,59 +398,132 @@ send_complete (krb5_context context, slave *s, } static int +send_are_you_there (krb5_context context, slave *s) +{ + krb5_storage *sp; + krb5_data data; + char buf[4]; + int ret; + + if (s->flags & (SLAVE_F_DEAD|SLAVE_F_AYT)) + return 0; + + s->flags |= SLAVE_F_AYT; + + data.data = buf; + data.length = 4; + + sp = krb5_storage_from_mem (buf, 4); + if (sp == NULL) { + krb5_warnx (context, "are_you_there: krb5_data_alloc"); + slave_dead(context, s); + return 1; + } + krb5_store_int32 (sp, ARE_YOU_THERE); + krb5_storage_free (sp); + + ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); + + if (ret) { + krb5_warn (context, ret, "are_you_there: krb5_write_priv_message"); + slave_dead(context, s); + return 1; + } + + return 0; +} + +static int send_diffs (krb5_context context, slave *s, int log_fd, - const char *database, u_int32_t current_version) + const char *database, uint32_t current_version) { krb5_storage *sp; - u_int32_t ver; + uint32_t ver; time_t timestamp; enum kadm_ops op; - u_int32_t len; + uint32_t len; off_t right, left; krb5_data data; int ret = 0; - if (s->version == current_version) + if (s->version == current_version) { + krb5_warnx(context, "slave %s in sync already at version %ld", + s->name, (long)s->version); return 0; + } if (s->flags & SLAVE_F_DEAD) return 0; + /* if slave is a fresh client, starting over */ + if (s->version == 0) { + krb5_warnx(context, "sending complete log to fresh slave %s", + s->name); + return send_complete (context, s, database, current_version); + } + sp = kadm5_log_goto_end (log_fd); right = krb5_storage_seek(sp, 0, SEEK_CUR); for (;;) { - if (kadm5_log_previous (sp, &ver, ×tamp, &op, &len)) - abort (); + ret = kadm5_log_previous (context, sp, &ver, ×tamp, &op, &len); + if (ret) + krb5_err(context, 1, ret, + "send_diffs: failed to find previous entry"); left = krb5_storage_seek(sp, -16, SEEK_CUR); if (ver == s->version) return 0; if (ver == s->version + 1) break; - if (left == 0) + if (left == 0) { + krb5_warnx(context, + "slave %s (version %lu) out of sync with master " + "(first version in log %lu), sending complete database", + s->name, (unsigned long)s->version, (unsigned long)ver); return send_complete (context, s, database, current_version); + } + } + + krb5_warnx(context, + "syncing slave %s from version %lu to version %lu", + s->name, (unsigned long)s->version, + (unsigned long)current_version); + + ret = krb5_data_alloc (&data, right - left + 4); + if (ret) { + krb5_warn (context, ret, "send_diffs: krb5_data_alloc"); + slave_dead(context, s); + return 1; } - krb5_data_alloc (&data, right - left + 4); krb5_storage_read (sp, (char *)data.data + 4, data.length - 4); krb5_storage_free(sp); - _krb5_put_int(data.data, FOR_YOU, 4); + sp = krb5_storage_from_data (&data); + if (sp == NULL) { + krb5_warnx (context, "send_diffs: krb5_storage_from_data"); + slave_dead(context, s); + return 1; + } + krb5_store_int32 (sp, FOR_YOU); + krb5_storage_free(sp); ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); krb5_data_free(&data); if (ret) { - krb5_warn (context, ret, "krb5_write_priv_message"); - slave_dead(s); + krb5_warn (context, ret, "send_diffs: krb5_write_priv_message"); + slave_dead(context, s); return 1; } slave_seen(s); + s->version = current_version; + return 0; } static int process_msg (krb5_context context, slave *s, int log_fd, - const char *database, u_int32_t current_version) + const char *database, uint32_t current_version) { int ret = 0; krb5_data out; @@ -383,13 +537,42 @@ process_msg (krb5_context context, slave *s, int log_fd, } sp = krb5_storage_from_mem (out.data, out.length); - krb5_ret_int32 (sp, &tmp); + if (sp == NULL) { + krb5_warnx (context, "process_msg: no memory"); + krb5_data_free (&out); + return 1; + } + if (krb5_ret_int32 (sp, &tmp) != 0) { + krb5_warnx (context, "process_msg: client send too short command"); + krb5_data_free (&out); + return 1; + } switch (tmp) { case I_HAVE : - krb5_ret_int32 (sp, &tmp); - s->version = tmp; - ret = send_diffs (context, s, log_fd, database, current_version); + ret = krb5_ret_int32 (sp, &tmp); + if (ret != 0) { + krb5_warnx (context, "process_msg: client send too I_HAVE data"); + break; + } + /* new started slave that have old log */ + if (s->version == 0 && tmp != 0) { + if (s->version < tmp) { + krb5_warnx (context, "Slave %s have later version the master " + "OUT OF SYNC", s->name); + } else { + s->version = tmp; + } + } + if (tmp < s->version) { + krb5_warnx (context, "Slave claims to not have " + "version we already sent to it"); + } else { + ret = send_diffs (context, s, log_fd, database, current_version); + } + break; + case I_AM_HERE : break; + case ARE_YOU_THERE: case FOR_YOU : default : krb5_warnx (context, "Ignoring command %d", tmp); @@ -409,20 +592,60 @@ process_msg (krb5_context context, slave *s, int log_fd, #define SLAVE_STATUS "Status" #define SLAVE_SEEN "Last Seen" +static FILE * +open_stats(krb5_context context) +{ + char *statfile = NULL; + const char *fn; + FILE *f; + + if (slave_stats_file) + fn = slave_stats_file; + else { + asprintf(&statfile, "%s/slaves-stats", hdb_db_dir(context)); + fn = krb5_config_get_string_default(context, + NULL, + statfile, + "kdc", + "iprop-stats", + NULL); + } + f = fopen(fn, "w"); + if (statfile) + free(statfile); + + return f; +} + +static void +write_master_down(krb5_context context) +{ + char str[100]; + time_t t = time(NULL); + FILE *fp; + + fp = open_stats(context); + if (fp == NULL) + return; + krb5_format_time(context, t, str, sizeof(str), TRUE); + fprintf(fp, "master down at %s\n", str); + + fclose(fp); +} + static void -write_stats(krb5_context context, slave *slaves, u_int32_t current_version) +write_stats(krb5_context context, slave *slaves, uint32_t current_version) { char str[100]; rtbl_t tbl; time_t t = time(NULL); FILE *fp; - fp = fopen(slave_stats_file, "w"); + fp = open_stats(context); if (fp == NULL) return; - strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S", - localtime(&t)); + krb5_format_time(context, t, str, sizeof(str), TRUE); fprintf(fp, "Status for slaves, last updated: %s\n\n", str); fprintf(fp, "Master version: %lu\n\n", (unsigned long)current_version); @@ -463,9 +686,7 @@ write_stats(krb5_context context, slave *slaves, u_int32_t current_version) else rtbl_add_column_entry(tbl, SLAVE_STATUS, "Up"); - if (strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S %Z", - localtime(&slaves->seen)) == 0) - strlcpy(str, "Unknown time", sizeof(str)); + ret = krb5_format_time(context, slaves->seen, str, sizeof(str), TRUE); rtbl_add_column_entry(tbl, SLAVE_SEEN, str); slaves = slaves->next; @@ -483,13 +704,28 @@ static int version_flag; static int help_flag; static char *keytab_str = "HDB:"; static char *database; +static char *config_file; +static char *port_str; +static int detach_from_console = 0; static struct getargs args[] = { + { "config-file", 'c', arg_string, &config_file }, { "realm", 'r', arg_string, &realm }, { "keytab", 'k', arg_string, &keytab_str, "keytab to get authentication from", "kspec" }, { "database", 'd', arg_string, &database, "database", "file"}, - { "slave-stats-file", 0, arg_string, &slave_stats_file, "file"}, + { "slave-stats-file", 0, arg_string, &slave_stats_file, + "file for slave status information", "file"}, + { "time-missing", 0, arg_string, &slave_time_missing, + "time before slave is polled for presence", "time"}, + { "time-gone", 0, arg_string, &slave_time_gone, + "time of inactivity after which a slave is considered gone", "time"}, + { "port", 0, arg_string, &port_str, + "port ipropd will listen to", "port"}, + { "detach", 0, arg_flag, &detach_from_console, + "detach from console" }, + { "hostname", 0, arg_string, &master_hostname, + "hostname of master (if not same as hostname)", "hostname" }, { "version", 0, arg_flag, &version_flag }, { "help", 0, arg_flag, &help_flag } }; @@ -506,11 +742,12 @@ main(int argc, char **argv) int signal_fd, listen_fd; int log_fd; slave *slaves = NULL; - u_int32_t current_version, old_version = 0; + uint32_t current_version = 0, old_version = 0; krb5_keytab keytab; - int optind; + int optidx; + char **files; - optind = krb5_program_setup(&context, argc, argv, args, num_args, NULL); + optidx = krb5_program_setup(&context, argc, argv, args, num_args, NULL); if(help_flag) krb5_std_usage(0, args, num_args); @@ -519,6 +756,32 @@ main(int argc, char **argv) exit(0); } + setup_signal(); + + if (config_file == NULL) { + asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context)); + if (config_file == NULL) + errx(1, "out of memory"); + } + + ret = krb5_prepend_config_files_default(config_file, &files); + if (ret) + krb5_err(context, 1, ret, "getting configuration files"); + + ret = krb5_set_config_files(context, files); + krb5_free_config_files(files); + if (ret) + krb5_err(context, 1, ret, "reading configuration files"); + + time_before_gone = parse_time (slave_time_gone, "s"); + if (time_before_gone < 0) + krb5_errx (context, 1, "couldn't parse time: %s", slave_time_gone); + time_before_missing = parse_time (slave_time_missing, "s"); + if (time_before_missing < 0) + krb5_errx (context, 1, "couldn't parse time: %s", slave_time_missing); + + if (detach_from_console) + daemon(0, 0); pidfile (NULL); krb5_openlog (context, "ipropd-master", &log_facility); krb5_set_warn_dest(context, log_facility); @@ -553,16 +816,19 @@ main(int argc, char **argv) server_context->log_context.log_file); signal_fd = make_signal_socket (context); - listen_fd = make_listen_socket (context); + listen_fd = make_listen_socket (context, port_str); - signal (SIGPIPE, SIG_IGN); + kadm5_log_get_version_fd (log_fd, ¤t_version); - for (;;) { + krb5_warnx(context, "ipropd-master started at version: %lu", + (unsigned long)current_version); + + while(exit_flag == 0){ slave *p; fd_set readset; int max_fd = 0; struct timeval to = {30, 0}; - u_int32_t vers; + uint32_t vers; if (signal_fd >= FD_SETSIZE || listen_fd >= FD_SETSIZE) krb5_errx (context, 1, "fd too large"); @@ -593,12 +859,17 @@ main(int argc, char **argv) old_version = current_version; kadm5_log_get_version_fd (log_fd, ¤t_version); - if (current_version > old_version) + if (current_version > old_version) { + krb5_warnx(context, + "Missed a signal, updating slaves %lu to %lu", + (unsigned long)old_version, + (unsigned long)current_version); for (p = slaves; p != NULL; p = p->next) { if (p->flags & SLAVE_F_DEAD) continue; send_diffs (context, p, log_fd, database, current_version); } + } } if (ret && FD_ISSET(signal_fd, &readset)) { @@ -611,28 +882,56 @@ main(int argc, char **argv) continue; } --ret; + assert(ret >= 0); old_version = current_version; kadm5_log_get_version_fd (log_fd, ¤t_version); - for (p = slaves; p != NULL; p = p->next) - send_diffs (context, p, log_fd, database, current_version); - } + if (current_version > old_version) { + krb5_warnx(context, + "Got a signal, updating slaves %lu to %lu", + (unsigned long)old_version, + (unsigned long)current_version); + for (p = slaves; p != NULL; p = p->next) + send_diffs (context, p, log_fd, database, current_version); + } else { + krb5_warnx(context, + "Got a signal, but no update in log version %lu", + (unsigned long)current_version); + } + } - for(p = slaves; ret && p != NULL; p = p->next) { + for(p = slaves; p != NULL; p = p->next) { if (p->flags & SLAVE_F_DEAD) - continue; - if (FD_ISSET(p->fd, &readset)) { + continue; + if (ret && FD_ISSET(p->fd, &readset)) { --ret; + assert(ret >= 0); if(process_msg (context, p, log_fd, database, current_version)) - slave_dead(p); + slave_dead(context, p); + } else if (slave_gone_p (p)) + slave_dead(context, p); + else if (slave_missing_p (p)) { + krb5_warnx(context, "slave %s missing, sending AYT", p->name); + send_are_you_there (context, p); } } if (ret && FD_ISSET(listen_fd, &readset)) { add_slave (context, keytab, &slaves, listen_fd); --ret; + assert(ret >= 0); } write_stats(context, slaves, current_version); } + if(exit_flag == SIGXCPU) + krb5_warnx(context, "%s CPU time limit exceeded", getprogname()); + else if(exit_flag == SIGINT || exit_flag == SIGTERM) + krb5_warnx(context, "%s terminated", getprogname()); + else + krb5_warnx(context, "%s unexpected exit reason: %d", + getprogname(), exit_flag); + + write_master_down(context); + return 0; } |