ipropd_master.c revision 90926
155682Smarkm/* 278527Sassar * Copyright (c) 1997 - 2001 Kungliga Tekniska H�gskolan 355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden). 455682Smarkm * All rights reserved. 555682Smarkm * 655682Smarkm * Redistribution and use in source and binary forms, with or without 755682Smarkm * modification, are permitted provided that the following conditions 855682Smarkm * are met: 955682Smarkm * 1055682Smarkm * 1. Redistributions of source code must retain the above copyright 1155682Smarkm * notice, this list of conditions and the following disclaimer. 1255682Smarkm * 1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright 1455682Smarkm * notice, this list of conditions and the following disclaimer in the 1555682Smarkm * documentation and/or other materials provided with the distribution. 1655682Smarkm * 1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors 1855682Smarkm * may be used to endorse or promote products derived from this software 1955682Smarkm * without specific prior written permission. 2055682Smarkm * 2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2455682Smarkm * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3155682Smarkm * SUCH DAMAGE. 3255682Smarkm */ 3355682Smarkm 3455682Smarkm#include "iprop.h" 3555682Smarkm 3690926SnectarRCSID("$Id: ipropd_master.c,v 1.24 2001/09/03 05:54:18 assar Exp $"); 3755682Smarkm 3872445Sassarstatic krb5_log_facility *log_facility; 3972445Sassar 4055682Smarkmstatic int 4155682Smarkmmake_signal_socket (krb5_context context) 4255682Smarkm{ 4355682Smarkm struct sockaddr_un addr; 4455682Smarkm int fd; 4555682Smarkm 4655682Smarkm fd = socket (AF_UNIX, SOCK_DGRAM, 0); 4755682Smarkm if (fd < 0) 4855682Smarkm krb5_err (context, 1, errno, "socket AF_UNIX"); 4955682Smarkm memset (&addr, 0, sizeof(addr)); 5055682Smarkm addr.sun_family = AF_UNIX; 5172445Sassar strlcpy (addr.sun_path, KADM5_LOG_SIGNAL, sizeof(addr.sun_path)); 5255682Smarkm unlink (addr.sun_path); 5355682Smarkm if (bind (fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) 5455682Smarkm krb5_err (context, 1, errno, "bind %s", addr.sun_path); 5555682Smarkm return fd; 5655682Smarkm} 5755682Smarkm 5855682Smarkmstatic int 5955682Smarkmmake_listen_socket (krb5_context context) 6055682Smarkm{ 6155682Smarkm int fd; 6255682Smarkm int one = 1; 6355682Smarkm struct sockaddr_in addr; 6455682Smarkm 6555682Smarkm fd = socket (AF_INET, SOCK_STREAM, 0); 6655682Smarkm if (fd < 0) 6755682Smarkm krb5_err (context, 1, errno, "socket AF_INET"); 6890926Snectar setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)); 6955682Smarkm memset (&addr, 0, sizeof(addr)); 7055682Smarkm addr.sin_family = AF_INET; 7172445Sassar addr.sin_port = krb5_getportbyname (context, 7272445Sassar IPROP_SERVICE, "tcp", IPROP_PORT); 7355682Smarkm if(bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) 7455682Smarkm krb5_err (context, 1, errno, "bind"); 7555682Smarkm if (listen(fd, SOMAXCONN) < 0) 7655682Smarkm krb5_err (context, 1, errno, "listen"); 7755682Smarkm return fd; 7855682Smarkm} 7955682Smarkm 8055682Smarkmstruct slave { 8155682Smarkm int fd; 8255682Smarkm struct sockaddr_in addr; 8355682Smarkm char *name; 8455682Smarkm krb5_auth_context ac; 8555682Smarkm u_int32_t version; 8655682Smarkm struct slave *next; 8755682Smarkm}; 8855682Smarkm 8955682Smarkmtypedef struct slave slave; 9055682Smarkm 9155682Smarkmstatic int 9255682Smarkmcheck_acl (krb5_context context, const char *name) 9355682Smarkm{ 9455682Smarkm FILE *fp; 9555682Smarkm char buf[256]; 9655682Smarkm int ret = 1; 9755682Smarkm 9855682Smarkm fp = fopen (KADM5_SLAVE_ACL, "r"); 9955682Smarkm if (fp == NULL) 10055682Smarkm return 1; 10155682Smarkm while (fgets(buf, sizeof(buf), fp) != NULL) { 10255682Smarkm if (buf[strlen(buf) - 1 ] == '\n') 10355682Smarkm buf[strlen(buf) - 1 ] = '\0'; 10455682Smarkm if (strcmp (buf, name) == 0) { 10555682Smarkm ret = 0; 10655682Smarkm break; 10755682Smarkm } 10855682Smarkm } 10955682Smarkm fclose (fp); 11055682Smarkm return ret; 11155682Smarkm} 11255682Smarkm 11355682Smarkmstatic void 11472445Sassaradd_slave (krb5_context context, krb5_keytab keytab, slave **root, int fd) 11555682Smarkm{ 11655682Smarkm krb5_principal server; 11755682Smarkm krb5_error_code ret; 11855682Smarkm slave *s; 11972445Sassar socklen_t addr_len; 12055682Smarkm krb5_ticket *ticket = NULL; 12155682Smarkm char hostname[128]; 12255682Smarkm 12355682Smarkm s = malloc(sizeof(*s)); 12455682Smarkm if (s == NULL) { 12555682Smarkm krb5_warnx (context, "add_slave: no memory"); 12655682Smarkm return; 12755682Smarkm } 12855682Smarkm s->name = NULL; 12955682Smarkm s->ac = NULL; 13055682Smarkm 13155682Smarkm addr_len = sizeof(s->addr); 13255682Smarkm s->fd = accept (fd, (struct sockaddr *)&s->addr, &addr_len); 13355682Smarkm if (s->fd < 0) { 13455682Smarkm krb5_warn (context, errno, "accept"); 13555682Smarkm goto error; 13655682Smarkm } 13755682Smarkm gethostname(hostname, sizeof(hostname)); 13855682Smarkm ret = krb5_sname_to_principal (context, hostname, IPROP_NAME, 13955682Smarkm KRB5_NT_SRV_HST, &server); 14055682Smarkm if (ret) { 14155682Smarkm krb5_warn (context, ret, "krb5_sname_to_principal"); 14255682Smarkm goto error; 14355682Smarkm } 14455682Smarkm 14555682Smarkm ret = krb5_recvauth (context, &s->ac, &s->fd, 14672445Sassar IPROP_VERSION, server, 0, keytab, &ticket); 14755682Smarkm krb5_free_principal (context, server); 14855682Smarkm if (ret) { 14955682Smarkm krb5_warn (context, ret, "krb5_recvauth"); 15055682Smarkm goto error; 15155682Smarkm } 15255682Smarkm ret = krb5_unparse_name (context, ticket->client, &s->name); 15355682Smarkm if (ret) { 15455682Smarkm krb5_warn (context, ret, "krb5_unparse_name"); 15555682Smarkm goto error; 15655682Smarkm } 15755682Smarkm if (check_acl (context, s->name)) { 15855682Smarkm krb5_warnx (context, "%s not in acl", s->name); 15955682Smarkm goto error; 16055682Smarkm } 16155682Smarkm krb5_free_ticket (context, ticket); 16272445Sassar krb5_warnx (context, "connection from %s", s->name); 16355682Smarkm 16455682Smarkm s->version = 0; 16555682Smarkm s->next = *root; 16655682Smarkm *root = s; 16755682Smarkm return; 16855682Smarkmerror: 16955682Smarkm if (s->name) 17055682Smarkm free (s->name); 17155682Smarkm if (s->ac) 17255682Smarkm krb5_auth_con_free(context, s->ac); 17355682Smarkm if (ticket) 17455682Smarkm krb5_free_ticket (context, ticket); 17555682Smarkm close (s->fd); 17655682Smarkm free(s); 17755682Smarkm} 17855682Smarkm 17955682Smarkmstatic void 18055682Smarkmremove_slave (krb5_context context, slave *s, slave **root) 18155682Smarkm{ 18255682Smarkm slave **p; 18355682Smarkm 18455682Smarkm close (s->fd); 18555682Smarkm free (s->name); 18655682Smarkm krb5_auth_con_free (context, s->ac); 18755682Smarkm 18855682Smarkm for (p = root; *p; p = &(*p)->next) 18955682Smarkm if (*p == s) { 19055682Smarkm *p = s->next; 19155682Smarkm break; 19255682Smarkm } 19355682Smarkm free (s); 19455682Smarkm} 19555682Smarkm 19672445Sassarstruct prop_context { 19772445Sassar krb5_auth_context auth_context; 19872445Sassar int fd; 19972445Sassar}; 20072445Sassar 20155682Smarkmstatic int 20272445Sassarprop_one (krb5_context context, HDB *db, hdb_entry *entry, void *v) 20355682Smarkm{ 20472445Sassar krb5_error_code ret; 20572445Sassar krb5_data data; 20672445Sassar struct slave *slave = (struct slave *)v; 20772445Sassar 20872445Sassar ret = hdb_entry2value (context, entry, &data); 20972445Sassar if (ret) 21072445Sassar return ret; 21172445Sassar ret = krb5_data_realloc (&data, data.length + 4); 21272445Sassar if (ret) { 21372445Sassar krb5_data_free (&data); 21472445Sassar return ret; 21572445Sassar } 21672445Sassar memmove ((char *)data.data + 4, data.data, data.length - 4); 21772445Sassar _krb5_put_int (data.data, ONE_PRINC, 4); 21872445Sassar 21972445Sassar ret = krb5_write_priv_message (context, slave->ac, &slave->fd, &data); 22072445Sassar krb5_data_free (&data); 22172445Sassar return ret; 22255682Smarkm} 22355682Smarkm 22455682Smarkmstatic int 22572445Sassarsend_complete (krb5_context context, slave *s, 22672445Sassar const char *database, u_int32_t current_version) 22772445Sassar{ 22872445Sassar krb5_error_code ret; 22972445Sassar HDB *db; 23072445Sassar krb5_data data; 23172445Sassar char buf[8]; 23272445Sassar 23372445Sassar ret = hdb_create (context, &db, database); 23472445Sassar if (ret) 23572445Sassar krb5_err (context, 1, ret, "hdb_create: %s", database); 23672445Sassar ret = db->open (context, db, O_RDONLY, 0); 23772445Sassar if (ret) 23872445Sassar krb5_err (context, 1, ret, "db->open"); 23972445Sassar 24072445Sassar _krb5_put_int(buf, TELL_YOU_EVERYTHING, 4); 24172445Sassar 24272445Sassar data.data = buf; 24372445Sassar data.length = 4; 24472445Sassar 24572445Sassar ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); 24672445Sassar 24772445Sassar if (ret) 24872445Sassar krb5_err (context, 1, ret, "krb5_write_priv_message"); 24972445Sassar 25072445Sassar ret = hdb_foreach (context, db, 0, prop_one, s); 25172445Sassar if (ret) 25272445Sassar krb5_err (context, 1, ret, "hdb_foreach"); 25372445Sassar 25472445Sassar _krb5_put_int (buf, NOW_YOU_HAVE, 4); 25572445Sassar _krb5_put_int (buf + 4, current_version, 4); 25672445Sassar data.length = 8; 25772445Sassar 25872445Sassar ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); 25972445Sassar 26072445Sassar if (ret) 26172445Sassar krb5_err (context, 1, ret, "krb5_write_priv_message"); 26272445Sassar 26372445Sassar return 0; 26472445Sassar} 26572445Sassar 26672445Sassarstatic int 26755682Smarkmsend_diffs (krb5_context context, slave *s, int log_fd, 26872445Sassar const char *database, u_int32_t current_version) 26955682Smarkm{ 27072445Sassar krb5_storage *sp; 27155682Smarkm u_int32_t ver; 27255682Smarkm time_t timestamp; 27355682Smarkm enum kadm_ops op; 27455682Smarkm u_int32_t len; 27555682Smarkm off_t right, left; 27655682Smarkm krb5_data data; 27755682Smarkm int ret = 0; 27855682Smarkm 27955682Smarkm if (s->version == current_version) 28055682Smarkm return 0; 28155682Smarkm 28255682Smarkm sp = kadm5_log_goto_end (log_fd); 28355682Smarkm right = sp->seek(sp, 0, SEEK_CUR); 28455682Smarkm for (;;) { 28555682Smarkm if (kadm5_log_previous (sp, &ver, ×tamp, &op, &len)) 28655682Smarkm abort (); 28755682Smarkm left = sp->seek(sp, -16, SEEK_CUR); 28855682Smarkm if (ver == s->version) 28955682Smarkm return 0; 29055682Smarkm if (ver == s->version + 1) 29155682Smarkm break; 29255682Smarkm if (left == 0) 29372445Sassar return send_complete (context, s, database, current_version); 29455682Smarkm } 29555682Smarkm krb5_data_alloc (&data, right - left + 4); 29655682Smarkm sp->fetch (sp, (char *)data.data + 4, data.length - 4); 29755682Smarkm krb5_storage_free(sp); 29855682Smarkm 29955682Smarkm _krb5_put_int(data.data, FOR_YOU, 4); 30055682Smarkm 30172445Sassar ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); 30255682Smarkm 30355682Smarkm if (ret) { 30472445Sassar krb5_warn (context, ret, "krb5_write_priv_message"); 30555682Smarkm return 1; 30655682Smarkm } 30755682Smarkm return 0; 30855682Smarkm} 30955682Smarkm 31055682Smarkmstatic int 31155682Smarkmprocess_msg (krb5_context context, slave *s, int log_fd, 31272445Sassar const char *database, u_int32_t current_version) 31355682Smarkm{ 31455682Smarkm int ret = 0; 31572445Sassar krb5_data out; 31655682Smarkm krb5_storage *sp; 31755682Smarkm int32_t tmp; 31855682Smarkm 31972445Sassar ret = krb5_read_priv_message(context, s->ac, &s->fd, &out); 32072445Sassar if(ret) { 32172445Sassar krb5_warn (context, ret, "error reading message from %s", s->name); 32255682Smarkm return 1; 32355682Smarkm } 32455682Smarkm 32555682Smarkm sp = krb5_storage_from_mem (out.data, out.length); 32655682Smarkm krb5_ret_int32 (sp, &tmp); 32755682Smarkm switch (tmp) { 32855682Smarkm case I_HAVE : 32955682Smarkm krb5_ret_int32 (sp, &tmp); 33055682Smarkm s->version = tmp; 33172445Sassar ret = send_diffs (context, s, log_fd, database, current_version); 33255682Smarkm break; 33355682Smarkm case FOR_YOU : 33455682Smarkm default : 33555682Smarkm krb5_warnx (context, "Ignoring command %d", tmp); 33655682Smarkm break; 33755682Smarkm } 33855682Smarkm 33955682Smarkm krb5_data_free (&out); 34055682Smarkm return ret; 34155682Smarkm} 34255682Smarkm 34372445Sassarstatic char *realm; 34472445Sassarstatic int version_flag; 34572445Sassarstatic int help_flag; 34672445Sassarstatic char *keytab_str = "HDB:"; 34772445Sassarstatic char *database; 34872445Sassar 34972445Sassarstatic struct getargs args[] = { 35055682Smarkm { "realm", 'r', arg_string, &realm }, 35172445Sassar { "keytab", 'k', arg_string, &keytab_str, 35272445Sassar "keytab to get authentication from", "kspec" }, 35372445Sassar { "database", 'd', arg_string, &database, "database", "file"}, 35455682Smarkm { "version", 0, arg_flag, &version_flag }, 35555682Smarkm { "help", 0, arg_flag, &help_flag } 35655682Smarkm}; 35772445Sassarstatic int num_args = sizeof(args) / sizeof(args[0]); 35855682Smarkm 35955682Smarkmint 36055682Smarkmmain(int argc, char **argv) 36155682Smarkm{ 36255682Smarkm krb5_error_code ret; 36355682Smarkm krb5_context context; 36455682Smarkm void *kadm_handle; 36555682Smarkm kadm5_server_context *server_context; 36655682Smarkm kadm5_config_params conf; 36755682Smarkm int signal_fd, listen_fd; 36855682Smarkm int log_fd; 36955682Smarkm slave *slaves = NULL; 37055682Smarkm u_int32_t current_version, old_version = 0; 37172445Sassar krb5_keytab keytab; 37255682Smarkm int optind; 37355682Smarkm 37455682Smarkm optind = krb5_program_setup(&context, argc, argv, args, num_args, NULL); 37555682Smarkm 37655682Smarkm if(help_flag) 37755682Smarkm krb5_std_usage(0, args, num_args); 37855682Smarkm if(version_flag) { 37955682Smarkm print_version(NULL); 38055682Smarkm exit(0); 38155682Smarkm } 38255682Smarkm 38390926Snectar pidfile (NULL); 38472445Sassar krb5_openlog (context, "ipropd-master", &log_facility); 38572445Sassar krb5_set_warn_dest(context, log_facility); 38672445Sassar 38772445Sassar ret = krb5_kt_register(context, &hdb_kt_ops); 38872445Sassar if(ret) 38972445Sassar krb5_err(context, 1, ret, "krb5_kt_register"); 39072445Sassar 39172445Sassar ret = krb5_kt_resolve(context, keytab_str, &keytab); 39272445Sassar if(ret) 39372445Sassar krb5_err(context, 1, ret, "krb5_kt_resolve: %s", keytab_str); 39472445Sassar 39555682Smarkm memset(&conf, 0, sizeof(conf)); 39655682Smarkm if(realm) { 39755682Smarkm conf.mask |= KADM5_CONFIG_REALM; 39855682Smarkm conf.realm = realm; 39955682Smarkm } 40072445Sassar ret = kadm5_init_with_skey_ctx (context, 40172445Sassar KADM5_ADMIN_SERVICE, 40272445Sassar NULL, 40372445Sassar KADM5_ADMIN_SERVICE, 40472445Sassar &conf, 0, 0, 40572445Sassar &kadm_handle); 40655682Smarkm if (ret) 40755682Smarkm krb5_err (context, 1, ret, "kadm5_init_with_password_ctx"); 40855682Smarkm 40955682Smarkm server_context = (kadm5_server_context *)kadm_handle; 41055682Smarkm 41155682Smarkm log_fd = open (server_context->log_context.log_file, O_RDONLY, 0); 41255682Smarkm if (log_fd < 0) 41355682Smarkm krb5_err (context, 1, errno, "open %s", 41455682Smarkm server_context->log_context.log_file); 41555682Smarkm 41655682Smarkm signal_fd = make_signal_socket (context); 41755682Smarkm listen_fd = make_listen_socket (context); 41855682Smarkm 41972445Sassar signal (SIGPIPE, SIG_IGN); 42072445Sassar 42155682Smarkm for (;;) { 42255682Smarkm slave *p; 42355682Smarkm fd_set readset; 42455682Smarkm int max_fd = 0; 42555682Smarkm struct timeval to = {30, 0}; 42655682Smarkm u_int32_t vers; 42755682Smarkm 42872445Sassar if (signal_fd >= FD_SETSIZE || listen_fd >= FD_SETSIZE) 42972445Sassar krb5_errx (context, 1, "fd too large"); 43072445Sassar 43155682Smarkm FD_ZERO(&readset); 43255682Smarkm FD_SET(signal_fd, &readset); 43355682Smarkm max_fd = max(max_fd, signal_fd); 43455682Smarkm FD_SET(listen_fd, &readset); 43555682Smarkm max_fd = max(max_fd, listen_fd); 43655682Smarkm 43755682Smarkm for (p = slaves; p != NULL; p = p->next) { 43855682Smarkm FD_SET(p->fd, &readset); 43955682Smarkm max_fd = max(max_fd, p->fd); 44055682Smarkm } 44155682Smarkm 44255682Smarkm ret = select (max_fd + 1, 44355682Smarkm &readset, NULL, NULL, &to); 44455682Smarkm if (ret < 0) { 44555682Smarkm if (errno == EINTR) 44655682Smarkm continue; 44755682Smarkm else 44855682Smarkm krb5_err (context, 1, errno, "select"); 44955682Smarkm } 45055682Smarkm 45155682Smarkm if (ret == 0) { 45255682Smarkm old_version = current_version; 45372445Sassar kadm5_log_get_version_fd (log_fd, ¤t_version); 45455682Smarkm 45555682Smarkm if (current_version > old_version) 45655682Smarkm for (p = slaves; p != NULL; p = p->next) 45772445Sassar send_diffs (context, p, log_fd, database, current_version); 45855682Smarkm } 45955682Smarkm 46055682Smarkm if (ret && FD_ISSET(signal_fd, &readset)) { 46155682Smarkm struct sockaddr_un peer_addr; 46272445Sassar socklen_t peer_len = sizeof(peer_addr); 46355682Smarkm 46490926Snectar if(recvfrom(signal_fd, (void *)&vers, sizeof(vers), 0, 46555682Smarkm (struct sockaddr *)&peer_addr, &peer_len) < 0) { 46655682Smarkm krb5_warn (context, errno, "recvfrom"); 46755682Smarkm continue; 46855682Smarkm } 46955682Smarkm --ret; 47055682Smarkm old_version = current_version; 47172445Sassar kadm5_log_get_version_fd (log_fd, ¤t_version); 47255682Smarkm for (p = slaves; p != NULL; p = p->next) 47372445Sassar send_diffs (context, p, log_fd, database, current_version); 47455682Smarkm } 47555682Smarkm 47678527Sassar for(p = slaves; p != NULL; p = p->next) 47755682Smarkm if (FD_ISSET(p->fd, &readset)) { 47878527Sassar --ret; 47972445Sassar if(process_msg (context, p, log_fd, database, current_version)) 48055682Smarkm remove_slave (context, p, &slaves); 48155682Smarkm } 48255682Smarkm 48355682Smarkm if (ret && FD_ISSET(listen_fd, &readset)) { 48472445Sassar add_slave (context, keytab, &slaves, listen_fd); 48555682Smarkm --ret; 48655682Smarkm } 48755682Smarkm 48855682Smarkm } 48955682Smarkm 49055682Smarkm return 0; 49155682Smarkm} 492