diff options
Diffstat (limited to 'crypto/heimdal/lib/kadm5/ipropd_slave.c')
-rw-r--r-- | crypto/heimdal/lib/kadm5/ipropd_slave.c | 327 |
1 files changed, 252 insertions, 75 deletions
diff --git a/crypto/heimdal/lib/kadm5/ipropd_slave.c b/crypto/heimdal/lib/kadm5/ipropd_slave.c index abeb29d..482a3f7 100644 --- a/crypto/heimdal/lib/kadm5/ipropd_slave.c +++ b/crypto/heimdal/lib/kadm5/ipropd_slave.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan + * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -33,12 +33,16 @@ #include "iprop.h" -RCSID("$Id: ipropd_slave.c,v 1.27.2.1 2003/08/15 16:45:15 lha Exp $"); +RCSID("$Id: ipropd_slave.c 22211 2007-12-07 19:27:27Z lha $"); static krb5_log_facility *log_facility; +static char *server_time_lost = "5 min"; +static int time_before_lost; +const char *slave_str = NULL; static int -connect_to_master (krb5_context context, const char *master) +connect_to_master (krb5_context context, const char *master, + const char *port_str) { int fd; struct sockaddr_in addr; @@ -49,8 +53,23 @@ connect_to_master (krb5_context context, const char *master) krb5_err (context, 1, errno, "socket AF_INET"); 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); + } he = roken_gethostbyname (master); if (he == NULL) krb5_errx (context, 1, "gethostbyname: %s", hstrerror(h_errno)); @@ -62,12 +81,12 @@ connect_to_master (krb5_context context, const char *master) static void get_creds(krb5_context context, const char *keytab_str, - krb5_ccache *cache, const char *host) + krb5_ccache *cache, const char *serverhost) { krb5_keytab keytab; krb5_principal client; krb5_error_code ret; - krb5_get_init_creds_opt init_opts; + krb5_get_init_creds_opt *init_opts; krb5_creds creds; char *server; char keytab_buf[256]; @@ -83,19 +102,22 @@ get_creds(krb5_context context, const char *keytab_str, if(ret) krb5_err(context, 1, ret, "%s", keytab_str); - ret = krb5_sname_to_principal (context, NULL, IPROP_NAME, + + ret = krb5_sname_to_principal (context, slave_str, IPROP_NAME, KRB5_NT_SRV_HST, &client); if (ret) krb5_err(context, 1, ret, "krb5_sname_to_principal"); - krb5_get_init_creds_opt_init(&init_opts); + ret = krb5_get_init_creds_opt_alloc(context, &init_opts); + if (ret) krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc"); - asprintf (&server, "%s/%s", IPROP_NAME, host); + asprintf (&server, "%s/%s", IPROP_NAME, serverhost); if (server == NULL) krb5_errx (context, 1, "malloc: no memory"); ret = krb5_get_init_creds_keytab(context, &creds, client, keytab, - 0, server, &init_opts); + 0, server, init_opts); free (server); + krb5_get_init_creds_opt_free(context, init_opts); if(ret) krb5_err(context, 1, ret, "krb5_get_init_creds"); ret = krb5_kt_close(context, keytab); @@ -113,12 +135,12 @@ get_creds(krb5_context context, const char *keytab_str, static void ihave (krb5_context context, krb5_auth_context auth_context, - int fd, u_int32_t version) + int fd, uint32_t version) { int ret; u_char buf[8]; krb5_storage *sp; - krb5_data data, priv_data; + krb5_data data; sp = krb5_storage_from_mem (buf, 8); krb5_store_int32 (sp, I_HAVE); @@ -127,15 +149,9 @@ ihave (krb5_context context, krb5_auth_context auth_context, data.length = 8; data.data = buf; - ret = krb5_mk_priv (context, auth_context, &data, &priv_data, NULL); + ret = krb5_write_priv_message(context, auth_context, &fd, &data); if (ret) - krb5_err (context, 1, ret, "krb_mk_priv"); - - ret = krb5_write_message (context, &fd, &priv_data); - if (ret) - krb5_err (context, 1, ret, "krb5_write_message"); - - krb5_data_free (&priv_data); + krb5_err (context, 1, ret, "krb5_write_priv_message"); } static void @@ -146,8 +162,12 @@ receive_loop (krb5_context context, int ret; off_t left, right; void *buf; - int32_t vers; + int32_t vers, vers2; + ssize_t sret; + /* + * Seek to the current version of the local database. + */ do { int32_t len, timestamp, tmp; enum kadm_ops op; @@ -159,43 +179,98 @@ receive_loop (krb5_context context, op = tmp; krb5_ret_int32 (sp, &len); if (vers <= server_context->log_context.version) - krb5_storage_seek(sp, len, SEEK_CUR); + krb5_storage_seek(sp, len + 8, SEEK_CUR); } while(vers <= server_context->log_context.version); + /* + * Read up rest of the entires into the memory... + */ left = krb5_storage_seek (sp, -16, SEEK_CUR); right = krb5_storage_seek (sp, 0, SEEK_END); buf = malloc (right - left); - if (buf == NULL && (right - left) != 0) { - krb5_warnx (context, "malloc: no memory"); - return; - } + if (buf == NULL && (right - left) != 0) + krb5_errx (context, 1, "malloc: no memory"); + + /* + * ...and then write them out to the on-disk log. + */ krb5_storage_seek (sp, left, SEEK_SET); krb5_storage_read (sp, buf, right - left); - write (server_context->log_context.log_fd, buf, right-left); - fsync (server_context->log_context.log_fd); + sret = write (server_context->log_context.log_fd, buf, right-left); + if (sret != right - left) + krb5_err(context, 1, errno, "Failed to write log to disk"); + ret = fsync (server_context->log_context.log_fd); + if (ret) + krb5_err(context, 1, errno, "Failed to sync log to disk"); free (buf); + /* + * Go back to the startpoint and start to commit the entires to + * the database. + */ krb5_storage_seek (sp, left, SEEK_SET); for(;;) { - int32_t len, timestamp, tmp; + int32_t len, len2, timestamp, tmp; + off_t cur, cur2; enum kadm_ops op; if(krb5_ret_int32 (sp, &vers) != 0) break; - krb5_ret_int32 (sp, ×tamp); - krb5_ret_int32 (sp, &tmp); + ret = krb5_ret_int32 (sp, ×tamp); + if (ret) krb5_errx(context, 1, "entry %ld: too short", (long)vers); + ret = krb5_ret_int32 (sp, &tmp); + if (ret) krb5_errx(context, 1, "entry %ld: too short", (long)vers); op = tmp; - krb5_ret_int32 (sp, &len); + ret = krb5_ret_int32 (sp, &len); + if (ret) krb5_errx(context, 1, "entry %ld: too short", (long)vers); + if (len < 0) + krb5_errx(context, 1, "log is corrupted, " + "negative length of entry version %ld: %ld", + (long)vers, (long)len); + cur = krb5_storage_seek(sp, 0, SEEK_CUR); + + krb5_warnx (context, "replaying entry %d", (int)vers); ret = kadm5_log_replay (server_context, op, vers, len, sp); - if (ret) - krb5_warn (context, ret, "kadm5_log_replay"); - else - server_context->log_context.version = vers; - krb5_storage_seek (sp, 8, SEEK_CUR); + if (ret) { + char *s = krb5_get_error_message(server_context->context, ret); + krb5_warnx (context, + "kadm5_log_replay: %ld. Lost entry entry, " + "Database out of sync ?: %s (%d)", + (long)vers, s ? s : "unknown error", ret); + krb5_xfree(s); + } + + { + /* + * Make sure the krb5_log_replay does the right thing wrt + * reading out data from the sp. + */ + cur2 = krb5_storage_seek(sp, 0, SEEK_CUR); + if (cur + len != cur2) + krb5_errx(context, 1, + "kadm5_log_reply version: %ld didn't read the whole entry", + (long)vers); + } + + if (krb5_ret_int32 (sp, &len2) != 0) + krb5_errx(context, 1, "entry %ld: postamble too short", (long)vers); + if(krb5_ret_int32 (sp, &vers2) != 0) + krb5_errx(context, 1, "entry %ld: postamble too short", (long)vers); + + if (len != len2) + krb5_errx(context, 1, "entry %ld: len != len2", (long)vers); + if (vers != vers2) + krb5_errx(context, 1, "entry %ld: vers != vers2", (long)vers); } + + /* + * Update version + */ + + server_context->log_context.version = vers; } static void @@ -205,20 +280,45 @@ receive (krb5_context context, { int ret; - ret = server_context->db->open(context, - server_context->db, - O_RDWR | O_CREAT, 0600); + ret = server_context->db->hdb_open(context, + server_context->db, + O_RDWR | O_CREAT, 0600); if (ret) krb5_err (context, 1, ret, "db->open"); receive_loop (context, sp, server_context); - ret = server_context->db->close (context, server_context->db); + ret = server_context->db->hdb_close (context, server_context->db); if (ret) krb5_err (context, 1, ret, "db->close"); } static void +send_im_here (krb5_context context, int fd, + krb5_auth_context auth_context) +{ + krb5_storage *sp; + krb5_data data; + int ret; + + ret = krb5_data_alloc (&data, 4); + if (ret) + krb5_err (context, 1, ret, "send_im_here"); + + sp = krb5_storage_from_data (&data); + if (sp == NULL) + krb5_errx (context, 1, "krb5_storage_from_data"); + krb5_store_int32(sp, I_AM_HERE); + krb5_storage_free(sp); + + ret = krb5_write_priv_message(context, auth_context, &fd, &data); + krb5_data_free(&data); + + if (ret) + krb5_err (context, 1, ret, "krb5_write_priv_message"); +} + +static void receive_everything (krb5_context context, int fd, kadm5_server_context *server_context, krb5_auth_context auth_context) @@ -227,12 +327,14 @@ receive_everything (krb5_context context, int fd, krb5_data data; int32_t vno; int32_t opcode; - unsigned long tmp; + krb5_storage *sp; char *dbname; HDB *mydb; - asprintf(&dbname, "%s-NEW", server_context->db->name); + krb5_warnx(context, "receive complete database"); + + asprintf(&dbname, "%s-NEW", server_context->db->hdb_name); ret = hdb_create(context, &mydb, dbname); if(ret) krb5_err(context,1, ret, "hdb_create"); @@ -245,47 +347,54 @@ receive_everything (krb5_context context, int fd, /* I really want to use O_EXCL here, but given that I can't easily clean up on error, I won't */ - ret = mydb->open(context, mydb, O_RDWR | O_CREAT | O_TRUNC, 0600); - + ret = mydb->hdb_open(context, mydb, O_RDWR | O_CREAT | O_TRUNC, 0600); if (ret) krb5_err (context, 1, ret, "db->open"); + sp = NULL; do { - krb5_storage *sp; - ret = krb5_read_priv_message(context, auth_context, &fd, &data); if (ret) krb5_err (context, 1, ret, "krb5_read_priv_message"); sp = krb5_storage_from_data (&data); + if (sp == NULL) + krb5_errx (context, 1, "krb5_storage_from_data"); krb5_ret_int32 (sp, &opcode); if (opcode == ONE_PRINC) { krb5_data fake_data; - hdb_entry entry; + hdb_entry_ex entry; + + krb5_storage_free(sp); fake_data.data = (char *)data.data + 4; fake_data.length = data.length - 4; - ret = hdb_value2entry (context, &fake_data, &entry); + memset(&entry, 0, sizeof(entry)); + + ret = hdb_value2entry (context, &fake_data, &entry.entry); if (ret) krb5_err (context, 1, ret, "hdb_value2entry"); - ret = mydb->store(server_context->context, - mydb, - 0, &entry); + ret = mydb->hdb_store(server_context->context, + mydb, + 0, &entry); if (ret) krb5_err (context, 1, ret, "hdb_store"); hdb_free_entry (context, &entry); krb5_data_free (&data); - } + } else if (opcode == NOW_YOU_HAVE) + ; + else + krb5_errx (context, 1, "strange opcode %d", opcode); } while (opcode == ONE_PRINC); if (opcode != NOW_YOU_HAVE) krb5_errx (context, 1, "receive_everything: strange %d", opcode); - _krb5_get_int ((char *)data.data + 4, &tmp, 4); - vno = tmp; + krb5_ret_int32 (sp, &vno); + krb5_storage_free(sp); ret = kadm5_log_reinit (server_context); if (ret) @@ -301,41 +410,48 @@ receive_everything (krb5_context context, int fd, krb5_data_free (&data); - ret = mydb->rename (context, mydb, server_context->db->name); + ret = mydb->hdb_rename (context, mydb, server_context->db->hdb_name); if (ret) krb5_err (context, 1, ret, "db->rename"); - ret = mydb->close (context, mydb); + ret = mydb->hdb_close (context, mydb); if (ret) krb5_err (context, 1, ret, "db->close"); - ret = mydb->destroy (context, mydb); + ret = mydb->hdb_destroy (context, mydb); if (ret) krb5_err (context, 1, ret, "db->destroy"); + + krb5_warnx(context, "receive complete database, version %ld", (long)vno); } +static char *config_file; static char *realm; static int version_flag; static int help_flag; static char *keytab_str; +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" }, + { "time-lost", 0, arg_string, &server_time_lost, + "time before server is considered lost", "time" }, + { "port", 0, arg_string, &port_str, + "port ipropd-slave will connect to", "port"}, + { "detach", 0, arg_flag, &detach_from_console, + "detach from console" }, + { "hostname", 0, arg_string, &slave_str, + "hostname of slave (if not same as hostname)", "hostname" }, { "version", 0, arg_flag, &version_flag }, { "help", 0, arg_flag, &help_flag } }; static int num_args = sizeof(args) / sizeof(args[0]); -static void -usage (int code, struct getargs *args, int num_args) -{ - arg_printusage (args, num_args, NULL, "master"); - exit (code); -} - int main(int argc, char **argv) { @@ -348,27 +464,47 @@ main(int argc, char **argv) int master_fd; krb5_ccache ccache; krb5_principal server; + char **files; + int optidx; - int optind; const char *master; - optind = krb5_program_setup(&context, argc, argv, args, num_args, usage); + optidx = krb5_program_setup(&context, argc, argv, args, num_args, NULL); if(help_flag) - usage (0, args, num_args); + krb5_std_usage(0, args, num_args); if(version_flag) { print_version(NULL); exit(0); } - argc -= optind; - argv += optind; + 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"); + + argc -= optidx; + argv += optidx; if (argc != 1) - usage (1, args, num_args); + krb5_std_usage(1, args, num_args); master = argv[0]; + if (detach_from_console) + daemon(0, 0); pidfile (NULL); krb5_openlog (context, "ipropd-slave", &log_facility); krb5_set_warn_dest(context, log_facility); @@ -377,6 +513,10 @@ main(int argc, char **argv) if(ret) krb5_err(context, 1, ret, "krb5_kt_register"); + time_before_lost = parse_time (server_time_lost, "s"); + if (time_before_lost < 0) + krb5_errx (context, 1, "couldn't parse time: %s", server_time_lost); + memset(&conf, 0, sizeof(conf)); if(realm) { conf.mask |= KADM5_CONFIG_REALM; @@ -399,7 +539,7 @@ main(int argc, char **argv) get_creds(context, keytab_str, &ccache, master); - master_fd = connect_to_master (context, master); + master_fd = connect_to_master (context, master, port_str); ret = krb5_sname_to_principal (context, master, IPROP_NAME, KRB5_NT_SRV_HST, &server); @@ -414,14 +554,39 @@ main(int argc, char **argv) if (ret) krb5_err (context, 1, ret, "krb5_sendauth"); + krb5_warnx(context, "ipropd-slave started at version: %ld", + (long)server_context->log_context.version); + ihave (context, auth_context, master_fd, server_context->log_context.version); - for (;;) { - int ret; + while (exit_flag == 0) { krb5_data out; krb5_storage *sp; int32_t tmp; + fd_set readset; + struct timeval to; + + if (master_fd >= FD_SETSIZE) + krb5_errx (context, 1, "fd too large"); + + FD_ZERO(&readset); + FD_SET(master_fd, &readset); + + to.tv_sec = time_before_lost; + to.tv_usec = 0; + + ret = select (master_fd + 1, + &readset, NULL, NULL, &to); + if (ret < 0) { + if (errno == EINTR) + continue; + else + krb5_err (context, 1, errno, "select"); + } + if (ret == 0) + krb5_errx (context, 1, "server didn't send a message " + "in %d seconds", time_before_lost); ret = krb5_read_priv_message(context, auth_context, &master_fd, &out); @@ -440,9 +605,13 @@ main(int argc, char **argv) receive_everything (context, master_fd, server_context, auth_context); break; + case ARE_YOU_THERE : + send_im_here (context, master_fd, auth_context); + break; case NOW_YOU_HAVE : case I_HAVE : case ONE_PRINC : + case I_AM_HERE : default : krb5_warnx (context, "Ignoring command %d", tmp); break; @@ -451,5 +620,13 @@ main(int argc, char **argv) krb5_data_free (&out); } + 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); + return 0; } |