155682Smarkm/* 2178825Sdfr * Copyright (c) 1997 - 2007 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" 35102644Snectar#include <rtbl.h> 3655682Smarkm 37178825SdfrRCSID("$Id: ipropd_master.c 22211 2007-12-07 19:27:27Z lha $"); 3855682Smarkm 3972445Sassarstatic krb5_log_facility *log_facility; 4072445Sassar 41178825Sdfrconst char *slave_stats_file; 42178825Sdfrconst char *slave_time_missing = "2 min"; 43178825Sdfrconst char *slave_time_gone = "5 min"; 44120945Snectar 45178825Sdfrstatic int time_before_missing; 46178825Sdfrstatic int time_before_gone; 47178825Sdfr 48178825Sdfrconst char *master_hostname; 49178825Sdfr 5055682Smarkmstatic int 5155682Smarkmmake_signal_socket (krb5_context context) 5255682Smarkm{ 5355682Smarkm struct sockaddr_un addr; 54178825Sdfr const char *fn; 5555682Smarkm int fd; 5655682Smarkm 57178825Sdfr fn = kadm5_log_signal_socket(context); 58178825Sdfr 5955682Smarkm fd = socket (AF_UNIX, SOCK_DGRAM, 0); 6055682Smarkm if (fd < 0) 6155682Smarkm krb5_err (context, 1, errno, "socket AF_UNIX"); 6255682Smarkm memset (&addr, 0, sizeof(addr)); 6355682Smarkm addr.sun_family = AF_UNIX; 64178825Sdfr strlcpy (addr.sun_path, fn, sizeof(addr.sun_path)); 6555682Smarkm unlink (addr.sun_path); 6655682Smarkm if (bind (fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) 6755682Smarkm krb5_err (context, 1, errno, "bind %s", addr.sun_path); 6855682Smarkm return fd; 6955682Smarkm} 7055682Smarkm 7155682Smarkmstatic int 72178825Sdfrmake_listen_socket (krb5_context context, const char *port_str) 7355682Smarkm{ 7455682Smarkm int fd; 7555682Smarkm int one = 1; 7655682Smarkm struct sockaddr_in addr; 7755682Smarkm 7855682Smarkm fd = socket (AF_INET, SOCK_STREAM, 0); 7955682Smarkm if (fd < 0) 8055682Smarkm krb5_err (context, 1, errno, "socket AF_INET"); 8190926Snectar setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)); 8255682Smarkm memset (&addr, 0, sizeof(addr)); 8355682Smarkm addr.sin_family = AF_INET; 84178825Sdfr 85178825Sdfr if (port_str) { 86178825Sdfr addr.sin_port = krb5_getportbyname (context, 87178825Sdfr port_str, "tcp", 88178825Sdfr 0); 89178825Sdfr if (addr.sin_port == 0) { 90178825Sdfr char *ptr; 91178825Sdfr long port; 92178825Sdfr 93178825Sdfr port = strtol (port_str, &ptr, 10); 94178825Sdfr if (port == 0 && ptr == port_str) 95178825Sdfr krb5_errx (context, 1, "bad port `%s'", port_str); 96178825Sdfr addr.sin_port = htons(port); 97178825Sdfr } 98178825Sdfr } else { 99178825Sdfr addr.sin_port = krb5_getportbyname (context, IPROP_SERVICE, 100178825Sdfr "tcp", IPROP_PORT); 101178825Sdfr } 10255682Smarkm if(bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) 10355682Smarkm krb5_err (context, 1, errno, "bind"); 10455682Smarkm if (listen(fd, SOMAXCONN) < 0) 10555682Smarkm krb5_err (context, 1, errno, "listen"); 10655682Smarkm return fd; 10755682Smarkm} 10855682Smarkm 10955682Smarkmstruct slave { 11055682Smarkm int fd; 11155682Smarkm struct sockaddr_in addr; 11255682Smarkm char *name; 11355682Smarkm krb5_auth_context ac; 114178825Sdfr uint32_t version; 115102644Snectar time_t seen; 116102644Snectar unsigned long flags; 117102644Snectar#define SLAVE_F_DEAD 0x1 118178825Sdfr#define SLAVE_F_AYT 0x2 11955682Smarkm struct slave *next; 12055682Smarkm}; 12155682Smarkm 12255682Smarkmtypedef struct slave slave; 12355682Smarkm 12455682Smarkmstatic int 12555682Smarkmcheck_acl (krb5_context context, const char *name) 12655682Smarkm{ 127178825Sdfr const char *fn; 12855682Smarkm FILE *fp; 12955682Smarkm char buf[256]; 13055682Smarkm int ret = 1; 131178825Sdfr char *slavefile; 13255682Smarkm 133178825Sdfr asprintf(&slavefile, "%s/slaves", hdb_db_dir(context)); 134178825Sdfr 135178825Sdfr fn = krb5_config_get_string_default(context, 136178825Sdfr NULL, 137178825Sdfr slavefile, 138178825Sdfr "kdc", 139178825Sdfr "iprop-acl", 140178825Sdfr NULL); 141178825Sdfr 142178825Sdfr fp = fopen (fn, "r"); 143178825Sdfr free(slavefile); 14455682Smarkm if (fp == NULL) 14555682Smarkm return 1; 14655682Smarkm while (fgets(buf, sizeof(buf), fp) != NULL) { 147178825Sdfr buf[strcspn(buf, "\r\n")] = '\0'; 14855682Smarkm if (strcmp (buf, name) == 0) { 14955682Smarkm ret = 0; 15055682Smarkm break; 15155682Smarkm } 15255682Smarkm } 15355682Smarkm fclose (fp); 15455682Smarkm return ret; 15555682Smarkm} 15655682Smarkm 15755682Smarkmstatic void 158102644Snectarslave_seen(slave *s) 159102644Snectar{ 160178825Sdfr s->flags &= ~SLAVE_F_AYT; 161102644Snectar s->seen = time(NULL); 162102644Snectar} 163102644Snectar 164178825Sdfrstatic int 165178825Sdfrslave_missing_p (slave *s) 166178825Sdfr{ 167178825Sdfr if (time(NULL) > s->seen + time_before_missing) 168178825Sdfr return 1; 169178825Sdfr return 0; 170178825Sdfr} 171178825Sdfr 172178825Sdfrstatic int 173178825Sdfrslave_gone_p (slave *s) 174178825Sdfr{ 175178825Sdfr if (time(NULL) > s->seen + time_before_gone) 176178825Sdfr return 1; 177178825Sdfr return 0; 178178825Sdfr} 179178825Sdfr 180102644Snectarstatic void 181178825Sdfrslave_dead(krb5_context context, slave *s) 182102644Snectar{ 183178825Sdfr krb5_warnx(context, "slave %s dead", s->name); 184178825Sdfr 185120945Snectar if (s->fd >= 0) { 186120945Snectar close (s->fd); 187120945Snectar s->fd = -1; 188120945Snectar } 189102644Snectar s->flags |= SLAVE_F_DEAD; 190102644Snectar slave_seen(s); 191102644Snectar} 192102644Snectar 193102644Snectarstatic void 194102644Snectarremove_slave (krb5_context context, slave *s, slave **root) 195102644Snectar{ 196102644Snectar slave **p; 197102644Snectar 198102644Snectar if (s->fd >= 0) 199102644Snectar close (s->fd); 200102644Snectar if (s->name) 201102644Snectar free (s->name); 202102644Snectar if (s->ac) 203102644Snectar krb5_auth_con_free (context, s->ac); 204102644Snectar 205102644Snectar for (p = root; *p; p = &(*p)->next) 206102644Snectar if (*p == s) { 207102644Snectar *p = s->next; 208102644Snectar break; 209102644Snectar } 210102644Snectar free (s); 211102644Snectar} 212102644Snectar 213102644Snectarstatic void 21472445Sassaradd_slave (krb5_context context, krb5_keytab keytab, slave **root, int fd) 21555682Smarkm{ 21655682Smarkm krb5_principal server; 21755682Smarkm krb5_error_code ret; 21855682Smarkm slave *s; 21972445Sassar socklen_t addr_len; 22055682Smarkm krb5_ticket *ticket = NULL; 22155682Smarkm char hostname[128]; 22255682Smarkm 22355682Smarkm s = malloc(sizeof(*s)); 22455682Smarkm if (s == NULL) { 22555682Smarkm krb5_warnx (context, "add_slave: no memory"); 22655682Smarkm return; 22755682Smarkm } 22855682Smarkm s->name = NULL; 22955682Smarkm s->ac = NULL; 23055682Smarkm 23155682Smarkm addr_len = sizeof(s->addr); 23255682Smarkm s->fd = accept (fd, (struct sockaddr *)&s->addr, &addr_len); 23355682Smarkm if (s->fd < 0) { 23455682Smarkm krb5_warn (context, errno, "accept"); 23555682Smarkm goto error; 23655682Smarkm } 237178825Sdfr if (master_hostname) 238178825Sdfr strlcpy(hostname, master_hostname, sizeof(hostname)); 239178825Sdfr else 240178825Sdfr gethostname(hostname, sizeof(hostname)); 241178825Sdfr 24255682Smarkm ret = krb5_sname_to_principal (context, hostname, IPROP_NAME, 24355682Smarkm KRB5_NT_SRV_HST, &server); 24455682Smarkm if (ret) { 24555682Smarkm krb5_warn (context, ret, "krb5_sname_to_principal"); 24655682Smarkm goto error; 24755682Smarkm } 24855682Smarkm 24955682Smarkm ret = krb5_recvauth (context, &s->ac, &s->fd, 25072445Sassar IPROP_VERSION, server, 0, keytab, &ticket); 25155682Smarkm krb5_free_principal (context, server); 25255682Smarkm if (ret) { 25355682Smarkm krb5_warn (context, ret, "krb5_recvauth"); 25455682Smarkm goto error; 25555682Smarkm } 25655682Smarkm ret = krb5_unparse_name (context, ticket->client, &s->name); 25755682Smarkm if (ret) { 25855682Smarkm krb5_warn (context, ret, "krb5_unparse_name"); 25955682Smarkm goto error; 26055682Smarkm } 26155682Smarkm if (check_acl (context, s->name)) { 26255682Smarkm krb5_warnx (context, "%s not in acl", s->name); 26355682Smarkm goto error; 26455682Smarkm } 26555682Smarkm krb5_free_ticket (context, ticket); 266102644Snectar ticket = NULL; 267102644Snectar 268102644Snectar { 269102644Snectar slave *l = *root; 270102644Snectar 271102644Snectar while (l) { 272102644Snectar if (strcmp(l->name, s->name) == 0) 273102644Snectar break; 274102644Snectar l = l->next; 275102644Snectar } 276102644Snectar if (l) { 277102644Snectar if (l->flags & SLAVE_F_DEAD) { 278102644Snectar remove_slave(context, l, root); 279102644Snectar } else { 280102644Snectar krb5_warnx (context, "second connection from %s", s->name); 281102644Snectar goto error; 282102644Snectar } 283102644Snectar } 284102644Snectar } 285102644Snectar 28672445Sassar krb5_warnx (context, "connection from %s", s->name); 28755682Smarkm 28855682Smarkm s->version = 0; 289102644Snectar s->flags = 0; 290102644Snectar slave_seen(s); 29155682Smarkm s->next = *root; 29255682Smarkm *root = s; 29355682Smarkm return; 29455682Smarkmerror: 295102644Snectar remove_slave(context, s, root); 29655682Smarkm} 29755682Smarkm 29872445Sassarstruct prop_context { 29972445Sassar krb5_auth_context auth_context; 30072445Sassar int fd; 30172445Sassar}; 30272445Sassar 30355682Smarkmstatic int 304178825Sdfrprop_one (krb5_context context, HDB *db, hdb_entry_ex *entry, void *v) 30555682Smarkm{ 30672445Sassar krb5_error_code ret; 307178825Sdfr krb5_storage *sp; 30872445Sassar krb5_data data; 309178825Sdfr struct slave *s = (struct slave *)v; 31072445Sassar 311178825Sdfr ret = hdb_entry2value (context, &entry->entry, &data); 31272445Sassar if (ret) 31372445Sassar return ret; 31472445Sassar ret = krb5_data_realloc (&data, data.length + 4); 31572445Sassar if (ret) { 31672445Sassar krb5_data_free (&data); 31772445Sassar return ret; 31872445Sassar } 31972445Sassar memmove ((char *)data.data + 4, data.data, data.length - 4); 320178825Sdfr sp = krb5_storage_from_data(&data); 321178825Sdfr if (sp == NULL) { 322178825Sdfr krb5_data_free (&data); 323178825Sdfr return ENOMEM; 324178825Sdfr } 325178825Sdfr krb5_store_int32(sp, ONE_PRINC); 326178825Sdfr krb5_storage_free(sp); 32772445Sassar 328178825Sdfr ret = krb5_write_priv_message (context, s->ac, &s->fd, &data); 32972445Sassar krb5_data_free (&data); 33072445Sassar return ret; 33155682Smarkm} 33255682Smarkm 33355682Smarkmstatic int 33472445Sassarsend_complete (krb5_context context, slave *s, 335178825Sdfr const char *database, uint32_t current_version) 33672445Sassar{ 33772445Sassar krb5_error_code ret; 338178825Sdfr krb5_storage *sp; 33972445Sassar HDB *db; 34072445Sassar krb5_data data; 34172445Sassar char buf[8]; 34272445Sassar 34372445Sassar ret = hdb_create (context, &db, database); 34472445Sassar if (ret) 34572445Sassar krb5_err (context, 1, ret, "hdb_create: %s", database); 346178825Sdfr ret = db->hdb_open (context, db, O_RDONLY, 0); 34772445Sassar if (ret) 34872445Sassar krb5_err (context, 1, ret, "db->open"); 34972445Sassar 350178825Sdfr sp = krb5_storage_from_mem (buf, 4); 351178825Sdfr if (sp == NULL) 352178825Sdfr krb5_errx (context, 1, "krb5_storage_from_mem"); 353178825Sdfr krb5_store_int32 (sp, TELL_YOU_EVERYTHING); 354178825Sdfr krb5_storage_free (sp); 35572445Sassar 35672445Sassar data.data = buf; 35772445Sassar data.length = 4; 35872445Sassar 35972445Sassar ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); 36072445Sassar 361102644Snectar if (ret) { 362102644Snectar krb5_warn (context, ret, "krb5_write_priv_message"); 363178825Sdfr slave_dead(context, s); 364102644Snectar return ret; 365102644Snectar } 36672445Sassar 36772445Sassar ret = hdb_foreach (context, db, 0, prop_one, s); 368102644Snectar if (ret) { 369102644Snectar krb5_warn (context, ret, "hdb_foreach"); 370178825Sdfr slave_dead(context, s); 371102644Snectar return ret; 372102644Snectar } 37372445Sassar 374178825Sdfr (*db->hdb_close)(context, db); 375178825Sdfr (*db->hdb_destroy)(context, db); 376178825Sdfr 377178825Sdfr sp = krb5_storage_from_mem (buf, 8); 378178825Sdfr if (sp == NULL) 379178825Sdfr krb5_errx (context, 1, "krb5_storage_from_mem"); 380178825Sdfr krb5_store_int32 (sp, NOW_YOU_HAVE); 381178825Sdfr krb5_store_int32 (sp, current_version); 382178825Sdfr krb5_storage_free (sp); 383178825Sdfr 38472445Sassar data.length = 8; 38572445Sassar 386102644Snectar s->version = current_version; 387102644Snectar 38872445Sassar ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); 389102644Snectar if (ret) { 390178825Sdfr slave_dead(context, s); 391102644Snectar krb5_warn (context, ret, "krb5_write_priv_message"); 392102644Snectar return ret; 393102644Snectar } 39472445Sassar 395102644Snectar slave_seen(s); 39672445Sassar 39772445Sassar return 0; 39872445Sassar} 39972445Sassar 40072445Sassarstatic int 401178825Sdfrsend_are_you_there (krb5_context context, slave *s) 402178825Sdfr{ 403178825Sdfr krb5_storage *sp; 404178825Sdfr krb5_data data; 405178825Sdfr char buf[4]; 406178825Sdfr int ret; 407178825Sdfr 408178825Sdfr if (s->flags & (SLAVE_F_DEAD|SLAVE_F_AYT)) 409178825Sdfr return 0; 410178825Sdfr 411178825Sdfr s->flags |= SLAVE_F_AYT; 412178825Sdfr 413178825Sdfr data.data = buf; 414178825Sdfr data.length = 4; 415178825Sdfr 416178825Sdfr sp = krb5_storage_from_mem (buf, 4); 417178825Sdfr if (sp == NULL) { 418178825Sdfr krb5_warnx (context, "are_you_there: krb5_data_alloc"); 419178825Sdfr slave_dead(context, s); 420178825Sdfr return 1; 421178825Sdfr } 422178825Sdfr krb5_store_int32 (sp, ARE_YOU_THERE); 423178825Sdfr krb5_storage_free (sp); 424178825Sdfr 425178825Sdfr ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); 426178825Sdfr 427178825Sdfr if (ret) { 428178825Sdfr krb5_warn (context, ret, "are_you_there: krb5_write_priv_message"); 429178825Sdfr slave_dead(context, s); 430178825Sdfr return 1; 431178825Sdfr } 432178825Sdfr 433178825Sdfr return 0; 434178825Sdfr} 435178825Sdfr 436178825Sdfrstatic int 43755682Smarkmsend_diffs (krb5_context context, slave *s, int log_fd, 438178825Sdfr const char *database, uint32_t current_version) 43955682Smarkm{ 44072445Sassar krb5_storage *sp; 441178825Sdfr uint32_t ver; 44255682Smarkm time_t timestamp; 44355682Smarkm enum kadm_ops op; 444178825Sdfr uint32_t len; 44555682Smarkm off_t right, left; 44655682Smarkm krb5_data data; 44755682Smarkm int ret = 0; 44855682Smarkm 449178825Sdfr if (s->version == current_version) { 450178825Sdfr krb5_warnx(context, "slave %s in sync already at version %ld", 451178825Sdfr s->name, (long)s->version); 45255682Smarkm return 0; 453178825Sdfr } 45455682Smarkm 455102644Snectar if (s->flags & SLAVE_F_DEAD) 456102644Snectar return 0; 457102644Snectar 458178825Sdfr /* if slave is a fresh client, starting over */ 459178825Sdfr if (s->version == 0) { 460178825Sdfr krb5_warnx(context, "sending complete log to fresh slave %s", 461178825Sdfr s->name); 462178825Sdfr return send_complete (context, s, database, current_version); 463178825Sdfr } 464178825Sdfr 46555682Smarkm sp = kadm5_log_goto_end (log_fd); 466102644Snectar right = krb5_storage_seek(sp, 0, SEEK_CUR); 46755682Smarkm for (;;) { 468178825Sdfr ret = kadm5_log_previous (context, sp, &ver, ×tamp, &op, &len); 469178825Sdfr if (ret) 470178825Sdfr krb5_err(context, 1, ret, 471178825Sdfr "send_diffs: failed to find previous entry"); 472102644Snectar left = krb5_storage_seek(sp, -16, SEEK_CUR); 47355682Smarkm if (ver == s->version) 47455682Smarkm return 0; 47555682Smarkm if (ver == s->version + 1) 47655682Smarkm break; 477178825Sdfr if (left == 0) { 478178825Sdfr krb5_warnx(context, 479178825Sdfr "slave %s (version %lu) out of sync with master " 480178825Sdfr "(first version in log %lu), sending complete database", 481178825Sdfr s->name, (unsigned long)s->version, (unsigned long)ver); 48272445Sassar return send_complete (context, s, database, current_version); 483178825Sdfr } 48455682Smarkm } 485178825Sdfr 486178825Sdfr krb5_warnx(context, 487178825Sdfr "syncing slave %s from version %lu to version %lu", 488178825Sdfr s->name, (unsigned long)s->version, 489178825Sdfr (unsigned long)current_version); 490178825Sdfr 491178825Sdfr ret = krb5_data_alloc (&data, right - left + 4); 492178825Sdfr if (ret) { 493178825Sdfr krb5_warn (context, ret, "send_diffs: krb5_data_alloc"); 494178825Sdfr slave_dead(context, s); 495178825Sdfr return 1; 496178825Sdfr } 497102644Snectar krb5_storage_read (sp, (char *)data.data + 4, data.length - 4); 49855682Smarkm krb5_storage_free(sp); 49955682Smarkm 500178825Sdfr sp = krb5_storage_from_data (&data); 501178825Sdfr if (sp == NULL) { 502178825Sdfr krb5_warnx (context, "send_diffs: krb5_storage_from_data"); 503178825Sdfr slave_dead(context, s); 504178825Sdfr return 1; 505178825Sdfr } 506178825Sdfr krb5_store_int32 (sp, FOR_YOU); 507178825Sdfr krb5_storage_free(sp); 50855682Smarkm 50972445Sassar ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); 510102644Snectar krb5_data_free(&data); 51155682Smarkm 51255682Smarkm if (ret) { 513178825Sdfr krb5_warn (context, ret, "send_diffs: krb5_write_priv_message"); 514178825Sdfr slave_dead(context, s); 51555682Smarkm return 1; 51655682Smarkm } 517102644Snectar slave_seen(s); 518102644Snectar 519178825Sdfr s->version = current_version; 520178825Sdfr 52155682Smarkm return 0; 52255682Smarkm} 52355682Smarkm 52455682Smarkmstatic int 52555682Smarkmprocess_msg (krb5_context context, slave *s, int log_fd, 526178825Sdfr const char *database, uint32_t current_version) 52755682Smarkm{ 52855682Smarkm int ret = 0; 52972445Sassar krb5_data out; 53055682Smarkm krb5_storage *sp; 53155682Smarkm int32_t tmp; 53255682Smarkm 53372445Sassar ret = krb5_read_priv_message(context, s->ac, &s->fd, &out); 53472445Sassar if(ret) { 53572445Sassar krb5_warn (context, ret, "error reading message from %s", s->name); 53655682Smarkm return 1; 53755682Smarkm } 53855682Smarkm 53955682Smarkm sp = krb5_storage_from_mem (out.data, out.length); 540178825Sdfr if (sp == NULL) { 541178825Sdfr krb5_warnx (context, "process_msg: no memory"); 542178825Sdfr krb5_data_free (&out); 543178825Sdfr return 1; 544178825Sdfr } 545178825Sdfr if (krb5_ret_int32 (sp, &tmp) != 0) { 546178825Sdfr krb5_warnx (context, "process_msg: client send too short command"); 547178825Sdfr krb5_data_free (&out); 548178825Sdfr return 1; 549178825Sdfr } 55055682Smarkm switch (tmp) { 55155682Smarkm case I_HAVE : 552178825Sdfr ret = krb5_ret_int32 (sp, &tmp); 553178825Sdfr if (ret != 0) { 554178825Sdfr krb5_warnx (context, "process_msg: client send too I_HAVE data"); 555178825Sdfr break; 556178825Sdfr } 557178825Sdfr /* new started slave that have old log */ 558178825Sdfr if (s->version == 0 && tmp != 0) { 559178825Sdfr if (s->version < tmp) { 560178825Sdfr krb5_warnx (context, "Slave %s have later version the master " 561178825Sdfr "OUT OF SYNC", s->name); 562178825Sdfr } else { 563178825Sdfr s->version = tmp; 564178825Sdfr } 565178825Sdfr } 566178825Sdfr if (tmp < s->version) { 567178825Sdfr krb5_warnx (context, "Slave claims to not have " 568178825Sdfr "version we already sent to it"); 569178825Sdfr } else { 570178825Sdfr ret = send_diffs (context, s, log_fd, database, current_version); 571178825Sdfr } 57255682Smarkm break; 573178825Sdfr case I_AM_HERE : 574178825Sdfr break; 575178825Sdfr case ARE_YOU_THERE: 57655682Smarkm case FOR_YOU : 57755682Smarkm default : 57855682Smarkm krb5_warnx (context, "Ignoring command %d", tmp); 57955682Smarkm break; 58055682Smarkm } 58155682Smarkm 58255682Smarkm krb5_data_free (&out); 583102644Snectar 584102644Snectar slave_seen(s); 585102644Snectar 58655682Smarkm return ret; 58755682Smarkm} 58855682Smarkm 589102644Snectar#define SLAVE_NAME "Name" 590102644Snectar#define SLAVE_ADDRESS "Address" 591102644Snectar#define SLAVE_VERSION "Version" 592102644Snectar#define SLAVE_STATUS "Status" 593102644Snectar#define SLAVE_SEEN "Last Seen" 594102644Snectar 595178825Sdfrstatic FILE * 596178825Sdfropen_stats(krb5_context context) 597178825Sdfr{ 598178825Sdfr char *statfile = NULL; 599178825Sdfr const char *fn; 600178825Sdfr FILE *f; 601178825Sdfr 602178825Sdfr if (slave_stats_file) 603178825Sdfr fn = slave_stats_file; 604178825Sdfr else { 605178825Sdfr asprintf(&statfile, "%s/slaves-stats", hdb_db_dir(context)); 606178825Sdfr fn = krb5_config_get_string_default(context, 607178825Sdfr NULL, 608178825Sdfr statfile, 609178825Sdfr "kdc", 610178825Sdfr "iprop-stats", 611178825Sdfr NULL); 612178825Sdfr } 613178825Sdfr f = fopen(fn, "w"); 614178825Sdfr if (statfile) 615178825Sdfr free(statfile); 616178825Sdfr 617178825Sdfr return f; 618178825Sdfr} 619178825Sdfr 620102644Snectarstatic void 621178825Sdfrwrite_master_down(krb5_context context) 622102644Snectar{ 623120945Snectar char str[100]; 624178825Sdfr time_t t = time(NULL); 625178825Sdfr FILE *fp; 626178825Sdfr 627178825Sdfr fp = open_stats(context); 628178825Sdfr if (fp == NULL) 629178825Sdfr return; 630178825Sdfr krb5_format_time(context, t, str, sizeof(str), TRUE); 631178825Sdfr fprintf(fp, "master down at %s\n", str); 632178825Sdfr 633178825Sdfr fclose(fp); 634178825Sdfr} 635178825Sdfr 636178825Sdfrstatic void 637178825Sdfrwrite_stats(krb5_context context, slave *slaves, uint32_t current_version) 638178825Sdfr{ 639178825Sdfr char str[100]; 640102644Snectar rtbl_t tbl; 641102644Snectar time_t t = time(NULL); 642102644Snectar FILE *fp; 643102644Snectar 644178825Sdfr fp = open_stats(context); 645102644Snectar if (fp == NULL) 646102644Snectar return; 647102644Snectar 648178825Sdfr krb5_format_time(context, t, str, sizeof(str), TRUE); 649102644Snectar fprintf(fp, "Status for slaves, last updated: %s\n\n", str); 650102644Snectar 651102644Snectar fprintf(fp, "Master version: %lu\n\n", (unsigned long)current_version); 652102644Snectar 653102644Snectar tbl = rtbl_create(); 654102644Snectar if (tbl == NULL) { 655102644Snectar fclose(fp); 656102644Snectar return; 657102644Snectar } 658102644Snectar 659102644Snectar rtbl_add_column(tbl, SLAVE_NAME, 0); 660102644Snectar rtbl_add_column(tbl, SLAVE_ADDRESS, 0); 661102644Snectar rtbl_add_column(tbl, SLAVE_VERSION, RTBL_ALIGN_RIGHT); 662102644Snectar rtbl_add_column(tbl, SLAVE_STATUS, 0); 663102644Snectar rtbl_add_column(tbl, SLAVE_SEEN, 0); 664102644Snectar 665102644Snectar rtbl_set_prefix(tbl, " "); 666102644Snectar rtbl_set_column_prefix(tbl, SLAVE_NAME, ""); 667102644Snectar 668102644Snectar while (slaves) { 669102644Snectar krb5_address addr; 670102644Snectar krb5_error_code ret; 671102644Snectar rtbl_add_column_entry(tbl, SLAVE_NAME, slaves->name); 672102644Snectar ret = krb5_sockaddr2address (context, 673102644Snectar (struct sockaddr*)&slaves->addr, &addr); 674102644Snectar if(ret == 0) { 675102644Snectar krb5_print_address(&addr, str, sizeof(str), NULL); 676102644Snectar krb5_free_address(context, &addr); 677102644Snectar rtbl_add_column_entry(tbl, SLAVE_ADDRESS, str); 678102644Snectar } else 679102644Snectar rtbl_add_column_entry(tbl, SLAVE_ADDRESS, "<unknown>"); 680102644Snectar 681102644Snectar snprintf(str, sizeof(str), "%u", (unsigned)slaves->version); 682102644Snectar rtbl_add_column_entry(tbl, SLAVE_VERSION, str); 683102644Snectar 684102644Snectar if (slaves->flags & SLAVE_F_DEAD) 685102644Snectar rtbl_add_column_entry(tbl, SLAVE_STATUS, "Down"); 686102644Snectar else 687102644Snectar rtbl_add_column_entry(tbl, SLAVE_STATUS, "Up"); 688102644Snectar 689178825Sdfr ret = krb5_format_time(context, slaves->seen, str, sizeof(str), TRUE); 690102644Snectar rtbl_add_column_entry(tbl, SLAVE_SEEN, str); 691102644Snectar 692102644Snectar slaves = slaves->next; 693102644Snectar } 694102644Snectar 695102644Snectar rtbl_format(tbl, fp); 696102644Snectar rtbl_destroy(tbl); 697102644Snectar 698102644Snectar fclose(fp); 699102644Snectar} 700102644Snectar 701102644Snectar 70272445Sassarstatic char *realm; 70372445Sassarstatic int version_flag; 70472445Sassarstatic int help_flag; 70572445Sassarstatic char *keytab_str = "HDB:"; 70672445Sassarstatic char *database; 707178825Sdfrstatic char *config_file; 708178825Sdfrstatic char *port_str; 709178825Sdfrstatic int detach_from_console = 0; 71072445Sassar 71172445Sassarstatic struct getargs args[] = { 712178825Sdfr { "config-file", 'c', arg_string, &config_file }, 71355682Smarkm { "realm", 'r', arg_string, &realm }, 71472445Sassar { "keytab", 'k', arg_string, &keytab_str, 71572445Sassar "keytab to get authentication from", "kspec" }, 71672445Sassar { "database", 'd', arg_string, &database, "database", "file"}, 717178825Sdfr { "slave-stats-file", 0, arg_string, &slave_stats_file, 718178825Sdfr "file for slave status information", "file"}, 719178825Sdfr { "time-missing", 0, arg_string, &slave_time_missing, 720178825Sdfr "time before slave is polled for presence", "time"}, 721178825Sdfr { "time-gone", 0, arg_string, &slave_time_gone, 722178825Sdfr "time of inactivity after which a slave is considered gone", "time"}, 723178825Sdfr { "port", 0, arg_string, &port_str, 724178825Sdfr "port ipropd will listen to", "port"}, 725178825Sdfr { "detach", 0, arg_flag, &detach_from_console, 726178825Sdfr "detach from console" }, 727178825Sdfr { "hostname", 0, arg_string, &master_hostname, 728178825Sdfr "hostname of master (if not same as hostname)", "hostname" }, 72955682Smarkm { "version", 0, arg_flag, &version_flag }, 73055682Smarkm { "help", 0, arg_flag, &help_flag } 73155682Smarkm}; 73272445Sassarstatic int num_args = sizeof(args) / sizeof(args[0]); 73355682Smarkm 73455682Smarkmint 73555682Smarkmmain(int argc, char **argv) 73655682Smarkm{ 73755682Smarkm krb5_error_code ret; 73855682Smarkm krb5_context context; 73955682Smarkm void *kadm_handle; 74055682Smarkm kadm5_server_context *server_context; 74155682Smarkm kadm5_config_params conf; 74255682Smarkm int signal_fd, listen_fd; 74355682Smarkm int log_fd; 74455682Smarkm slave *slaves = NULL; 745178825Sdfr uint32_t current_version = 0, old_version = 0; 74672445Sassar krb5_keytab keytab; 747178825Sdfr int optidx; 748178825Sdfr char **files; 74955682Smarkm 750178825Sdfr optidx = krb5_program_setup(&context, argc, argv, args, num_args, NULL); 75155682Smarkm 75255682Smarkm if(help_flag) 75355682Smarkm krb5_std_usage(0, args, num_args); 75455682Smarkm if(version_flag) { 75555682Smarkm print_version(NULL); 75655682Smarkm exit(0); 75755682Smarkm } 75855682Smarkm 759178825Sdfr setup_signal(); 760178825Sdfr 761178825Sdfr if (config_file == NULL) { 762178825Sdfr asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context)); 763178825Sdfr if (config_file == NULL) 764178825Sdfr errx(1, "out of memory"); 765178825Sdfr } 766178825Sdfr 767178825Sdfr ret = krb5_prepend_config_files_default(config_file, &files); 768178825Sdfr if (ret) 769178825Sdfr krb5_err(context, 1, ret, "getting configuration files"); 770178825Sdfr 771178825Sdfr ret = krb5_set_config_files(context, files); 772178825Sdfr krb5_free_config_files(files); 773178825Sdfr if (ret) 774178825Sdfr krb5_err(context, 1, ret, "reading configuration files"); 775178825Sdfr 776178825Sdfr time_before_gone = parse_time (slave_time_gone, "s"); 777178825Sdfr if (time_before_gone < 0) 778178825Sdfr krb5_errx (context, 1, "couldn't parse time: %s", slave_time_gone); 779178825Sdfr time_before_missing = parse_time (slave_time_missing, "s"); 780178825Sdfr if (time_before_missing < 0) 781178825Sdfr krb5_errx (context, 1, "couldn't parse time: %s", slave_time_missing); 782178825Sdfr 783178825Sdfr if (detach_from_console) 784178825Sdfr daemon(0, 0); 78590926Snectar pidfile (NULL); 78672445Sassar krb5_openlog (context, "ipropd-master", &log_facility); 78772445Sassar krb5_set_warn_dest(context, log_facility); 78872445Sassar 78972445Sassar ret = krb5_kt_register(context, &hdb_kt_ops); 79072445Sassar if(ret) 79172445Sassar krb5_err(context, 1, ret, "krb5_kt_register"); 79272445Sassar 79372445Sassar ret = krb5_kt_resolve(context, keytab_str, &keytab); 79472445Sassar if(ret) 79572445Sassar krb5_err(context, 1, ret, "krb5_kt_resolve: %s", keytab_str); 79672445Sassar 79755682Smarkm memset(&conf, 0, sizeof(conf)); 79855682Smarkm if(realm) { 79955682Smarkm conf.mask |= KADM5_CONFIG_REALM; 80055682Smarkm conf.realm = realm; 80155682Smarkm } 80272445Sassar ret = kadm5_init_with_skey_ctx (context, 80372445Sassar KADM5_ADMIN_SERVICE, 80472445Sassar NULL, 80572445Sassar KADM5_ADMIN_SERVICE, 80672445Sassar &conf, 0, 0, 80772445Sassar &kadm_handle); 80855682Smarkm if (ret) 80955682Smarkm krb5_err (context, 1, ret, "kadm5_init_with_password_ctx"); 81055682Smarkm 81155682Smarkm server_context = (kadm5_server_context *)kadm_handle; 81255682Smarkm 81355682Smarkm log_fd = open (server_context->log_context.log_file, O_RDONLY, 0); 81455682Smarkm if (log_fd < 0) 81555682Smarkm krb5_err (context, 1, errno, "open %s", 81655682Smarkm server_context->log_context.log_file); 81755682Smarkm 81855682Smarkm signal_fd = make_signal_socket (context); 819178825Sdfr listen_fd = make_listen_socket (context, port_str); 82055682Smarkm 821178825Sdfr kadm5_log_get_version_fd (log_fd, ¤t_version); 82272445Sassar 823178825Sdfr krb5_warnx(context, "ipropd-master started at version: %lu", 824178825Sdfr (unsigned long)current_version); 825178825Sdfr 826178825Sdfr while(exit_flag == 0){ 82755682Smarkm slave *p; 82855682Smarkm fd_set readset; 82955682Smarkm int max_fd = 0; 83055682Smarkm struct timeval to = {30, 0}; 831178825Sdfr uint32_t vers; 83255682Smarkm 83372445Sassar if (signal_fd >= FD_SETSIZE || listen_fd >= FD_SETSIZE) 83472445Sassar krb5_errx (context, 1, "fd too large"); 83572445Sassar 83655682Smarkm FD_ZERO(&readset); 83755682Smarkm FD_SET(signal_fd, &readset); 83855682Smarkm max_fd = max(max_fd, signal_fd); 83955682Smarkm FD_SET(listen_fd, &readset); 84055682Smarkm max_fd = max(max_fd, listen_fd); 84155682Smarkm 84255682Smarkm for (p = slaves; p != NULL; p = p->next) { 843120945Snectar if (p->flags & SLAVE_F_DEAD) 844120945Snectar continue; 84555682Smarkm FD_SET(p->fd, &readset); 84655682Smarkm max_fd = max(max_fd, p->fd); 84755682Smarkm } 84855682Smarkm 84955682Smarkm ret = select (max_fd + 1, 85055682Smarkm &readset, NULL, NULL, &to); 85155682Smarkm if (ret < 0) { 85255682Smarkm if (errno == EINTR) 85355682Smarkm continue; 85455682Smarkm else 85555682Smarkm krb5_err (context, 1, errno, "select"); 85655682Smarkm } 85755682Smarkm 85855682Smarkm if (ret == 0) { 85955682Smarkm old_version = current_version; 86072445Sassar kadm5_log_get_version_fd (log_fd, ¤t_version); 86155682Smarkm 862178825Sdfr if (current_version > old_version) { 863178825Sdfr krb5_warnx(context, 864178825Sdfr "Missed a signal, updating slaves %lu to %lu", 865178825Sdfr (unsigned long)old_version, 866178825Sdfr (unsigned long)current_version); 867120945Snectar for (p = slaves; p != NULL; p = p->next) { 868120945Snectar if (p->flags & SLAVE_F_DEAD) 869120945Snectar continue; 87072445Sassar send_diffs (context, p, log_fd, database, current_version); 871120945Snectar } 872178825Sdfr } 87355682Smarkm } 87455682Smarkm 87555682Smarkm if (ret && FD_ISSET(signal_fd, &readset)) { 87655682Smarkm struct sockaddr_un peer_addr; 87772445Sassar socklen_t peer_len = sizeof(peer_addr); 87855682Smarkm 87990926Snectar if(recvfrom(signal_fd, (void *)&vers, sizeof(vers), 0, 88055682Smarkm (struct sockaddr *)&peer_addr, &peer_len) < 0) { 88155682Smarkm krb5_warn (context, errno, "recvfrom"); 88255682Smarkm continue; 88355682Smarkm } 88455682Smarkm --ret; 885178825Sdfr assert(ret >= 0); 88655682Smarkm old_version = current_version; 88772445Sassar kadm5_log_get_version_fd (log_fd, ¤t_version); 888178825Sdfr if (current_version > old_version) { 889178825Sdfr krb5_warnx(context, 890178825Sdfr "Got a signal, updating slaves %lu to %lu", 891178825Sdfr (unsigned long)old_version, 892178825Sdfr (unsigned long)current_version); 893178825Sdfr for (p = slaves; p != NULL; p = p->next) 894178825Sdfr send_diffs (context, p, log_fd, database, current_version); 895178825Sdfr } else { 896178825Sdfr krb5_warnx(context, 897178825Sdfr "Got a signal, but no update in log version %lu", 898178825Sdfr (unsigned long)current_version); 899178825Sdfr } 900178825Sdfr } 90155682Smarkm 902178825Sdfr for(p = slaves; p != NULL; p = p->next) { 903120945Snectar if (p->flags & SLAVE_F_DEAD) 904178825Sdfr continue; 905178825Sdfr if (ret && FD_ISSET(p->fd, &readset)) { 90678527Sassar --ret; 907178825Sdfr assert(ret >= 0); 90872445Sassar if(process_msg (context, p, log_fd, database, current_version)) 909178825Sdfr slave_dead(context, p); 910178825Sdfr } else if (slave_gone_p (p)) 911178825Sdfr slave_dead(context, p); 912178825Sdfr else if (slave_missing_p (p)) { 913178825Sdfr krb5_warnx(context, "slave %s missing, sending AYT", p->name); 914178825Sdfr send_are_you_there (context, p); 91555682Smarkm } 916120945Snectar } 91755682Smarkm 91855682Smarkm if (ret && FD_ISSET(listen_fd, &readset)) { 91972445Sassar add_slave (context, keytab, &slaves, listen_fd); 92055682Smarkm --ret; 921178825Sdfr assert(ret >= 0); 92255682Smarkm } 923102644Snectar write_stats(context, slaves, current_version); 92455682Smarkm } 92555682Smarkm 926178825Sdfr if(exit_flag == SIGXCPU) 927178825Sdfr krb5_warnx(context, "%s CPU time limit exceeded", getprogname()); 928178825Sdfr else if(exit_flag == SIGINT || exit_flag == SIGTERM) 929178825Sdfr krb5_warnx(context, "%s terminated", getprogname()); 930178825Sdfr else 931178825Sdfr krb5_warnx(context, "%s unexpected exit reason: %d", 932178825Sdfr getprogname(), exit_flag); 933178825Sdfr 934178825Sdfr write_master_down(context); 935178825Sdfr 93655682Smarkm return 0; 93755682Smarkm} 938