ipropd_master.c revision 233294
155682Smarkm/* 2233294Sstas * Copyright (c) 1997 - 2008 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4233294Sstas * All rights reserved. 555682Smarkm * 6233294Sstas * Redistribution and use in source and binary forms, with or without 7233294Sstas * modification, are permitted provided that the following conditions 8233294Sstas * are met: 955682Smarkm * 10233294Sstas * 1. Redistributions of source code must retain the above copyright 11233294Sstas * notice, this list of conditions and the following disclaimer. 1255682Smarkm * 13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 14233294Sstas * notice, this list of conditions and the following disclaimer in the 15233294Sstas * documentation and/or other materials provided with the distribution. 1655682Smarkm * 17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors 18233294Sstas * may be used to endorse or promote products derived from this software 19233294Sstas * without specific prior written permission. 2055682Smarkm * 21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24233294Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31233294Sstas * SUCH DAMAGE. 3255682Smarkm */ 3355682Smarkm 3455682Smarkm#include "iprop.h" 35102644Snectar#include <rtbl.h> 3655682Smarkm 3772445Sassarstatic krb5_log_facility *log_facility; 3872445Sassar 39178825Sdfrconst char *slave_stats_file; 40178825Sdfrconst char *slave_time_missing = "2 min"; 41178825Sdfrconst char *slave_time_gone = "5 min"; 42120945Snectar 43178825Sdfrstatic int time_before_missing; 44178825Sdfrstatic int time_before_gone; 45178825Sdfr 46178825Sdfrconst char *master_hostname; 47178825Sdfr 48233294Sstasstatic krb5_socket_t 4955682Smarkmmake_signal_socket (krb5_context context) 5055682Smarkm{ 51233294Sstas#ifndef NO_UNIX_SOCKETS 5255682Smarkm struct sockaddr_un addr; 53178825Sdfr const char *fn; 54233294Sstas krb5_socket_t fd; 5555682Smarkm 56178825Sdfr fn = kadm5_log_signal_socket(context); 57178825Sdfr 5855682Smarkm fd = socket (AF_UNIX, SOCK_DGRAM, 0); 5955682Smarkm if (fd < 0) 6055682Smarkm krb5_err (context, 1, errno, "socket AF_UNIX"); 6155682Smarkm memset (&addr, 0, sizeof(addr)); 6255682Smarkm addr.sun_family = AF_UNIX; 63178825Sdfr strlcpy (addr.sun_path, fn, sizeof(addr.sun_path)); 6455682Smarkm unlink (addr.sun_path); 6555682Smarkm if (bind (fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) 6655682Smarkm krb5_err (context, 1, errno, "bind %s", addr.sun_path); 6755682Smarkm return fd; 68233294Sstas#else 69233294Sstas struct addrinfo *ai = NULL; 70233294Sstas krb5_socket_t fd; 71233294Sstas 72233294Sstas kadm5_log_signal_socket_info(context, 1, &ai); 73233294Sstas 74233294Sstas fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 75233294Sstas if (rk_IS_BAD_SOCKET(fd)) 76233294Sstas krb5_err (context, 1, rk_SOCK_ERRNO, "socket AF=%d", ai->ai_family); 77233294Sstas 78233294Sstas if (rk_IS_SOCKET_ERROR( bind (fd, ai->ai_addr, ai->ai_addrlen) )) 79233294Sstas krb5_err (context, 1, rk_SOCK_ERRNO, "bind"); 80233294Sstas return fd; 81233294Sstas#endif 8255682Smarkm} 8355682Smarkm 84233294Sstasstatic krb5_socket_t 85178825Sdfrmake_listen_socket (krb5_context context, const char *port_str) 8655682Smarkm{ 87233294Sstas krb5_socket_t fd; 8855682Smarkm int one = 1; 8955682Smarkm struct sockaddr_in addr; 9055682Smarkm 9155682Smarkm fd = socket (AF_INET, SOCK_STREAM, 0); 92233294Sstas if (rk_IS_BAD_SOCKET(fd)) 93233294Sstas krb5_err (context, 1, rk_SOCK_ERRNO, "socket AF_INET"); 9490926Snectar setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)); 9555682Smarkm memset (&addr, 0, sizeof(addr)); 9655682Smarkm addr.sin_family = AF_INET; 97178825Sdfr 98178825Sdfr if (port_str) { 99178825Sdfr addr.sin_port = krb5_getportbyname (context, 100233294Sstas port_str, "tcp", 101178825Sdfr 0); 102178825Sdfr if (addr.sin_port == 0) { 103178825Sdfr char *ptr; 104178825Sdfr long port; 105178825Sdfr 106178825Sdfr port = strtol (port_str, &ptr, 10); 107178825Sdfr if (port == 0 && ptr == port_str) 108178825Sdfr krb5_errx (context, 1, "bad port `%s'", port_str); 109178825Sdfr addr.sin_port = htons(port); 110178825Sdfr } 111178825Sdfr } else { 112233294Sstas addr.sin_port = krb5_getportbyname (context, IPROP_SERVICE, 113178825Sdfr "tcp", IPROP_PORT); 114178825Sdfr } 11555682Smarkm if(bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) 11655682Smarkm krb5_err (context, 1, errno, "bind"); 11755682Smarkm if (listen(fd, SOMAXCONN) < 0) 11855682Smarkm krb5_err (context, 1, errno, "listen"); 11955682Smarkm return fd; 12055682Smarkm} 12155682Smarkm 12255682Smarkmstruct slave { 123233294Sstas krb5_socket_t fd; 12455682Smarkm struct sockaddr_in addr; 12555682Smarkm char *name; 12655682Smarkm krb5_auth_context ac; 127178825Sdfr uint32_t version; 128102644Snectar time_t seen; 129102644Snectar unsigned long flags; 130102644Snectar#define SLAVE_F_DEAD 0x1 131178825Sdfr#define SLAVE_F_AYT 0x2 13255682Smarkm struct slave *next; 13355682Smarkm}; 13455682Smarkm 13555682Smarkmtypedef struct slave slave; 13655682Smarkm 13755682Smarkmstatic int 13855682Smarkmcheck_acl (krb5_context context, const char *name) 13955682Smarkm{ 140178825Sdfr const char *fn; 14155682Smarkm FILE *fp; 14255682Smarkm char buf[256]; 14355682Smarkm int ret = 1; 144233294Sstas char *slavefile = NULL; 14555682Smarkm 146233294Sstas if (asprintf(&slavefile, "%s/slaves", hdb_db_dir(context)) == -1 147233294Sstas || slavefile == NULL) 148233294Sstas errx(1, "out of memory"); 149178825Sdfr 150178825Sdfr fn = krb5_config_get_string_default(context, 151178825Sdfr NULL, 152178825Sdfr slavefile, 153178825Sdfr "kdc", 154178825Sdfr "iprop-acl", 155178825Sdfr NULL); 156178825Sdfr 157178825Sdfr fp = fopen (fn, "r"); 158178825Sdfr free(slavefile); 15955682Smarkm if (fp == NULL) 16055682Smarkm return 1; 16155682Smarkm while (fgets(buf, sizeof(buf), fp) != NULL) { 162178825Sdfr buf[strcspn(buf, "\r\n")] = '\0'; 16355682Smarkm if (strcmp (buf, name) == 0) { 16455682Smarkm ret = 0; 16555682Smarkm break; 16655682Smarkm } 16755682Smarkm } 16855682Smarkm fclose (fp); 16955682Smarkm return ret; 17055682Smarkm} 17155682Smarkm 17255682Smarkmstatic void 173102644Snectarslave_seen(slave *s) 174102644Snectar{ 175178825Sdfr s->flags &= ~SLAVE_F_AYT; 176102644Snectar s->seen = time(NULL); 177102644Snectar} 178102644Snectar 179178825Sdfrstatic int 180178825Sdfrslave_missing_p (slave *s) 181178825Sdfr{ 182178825Sdfr if (time(NULL) > s->seen + time_before_missing) 183178825Sdfr return 1; 184178825Sdfr return 0; 185178825Sdfr} 186178825Sdfr 187178825Sdfrstatic int 188178825Sdfrslave_gone_p (slave *s) 189178825Sdfr{ 190178825Sdfr if (time(NULL) > s->seen + time_before_gone) 191178825Sdfr return 1; 192178825Sdfr return 0; 193178825Sdfr} 194178825Sdfr 195102644Snectarstatic void 196178825Sdfrslave_dead(krb5_context context, slave *s) 197102644Snectar{ 198178825Sdfr krb5_warnx(context, "slave %s dead", s->name); 199178825Sdfr 200233294Sstas if (!rk_IS_BAD_SOCKET(s->fd)) { 201233294Sstas rk_closesocket (s->fd); 202233294Sstas s->fd = rk_INVALID_SOCKET; 203120945Snectar } 204102644Snectar s->flags |= SLAVE_F_DEAD; 205102644Snectar slave_seen(s); 206102644Snectar} 207102644Snectar 208102644Snectarstatic void 209102644Snectarremove_slave (krb5_context context, slave *s, slave **root) 210102644Snectar{ 211102644Snectar slave **p; 212102644Snectar 213233294Sstas if (!rk_IS_BAD_SOCKET(s->fd)) 214233294Sstas rk_closesocket (s->fd); 215102644Snectar if (s->name) 216102644Snectar free (s->name); 217102644Snectar if (s->ac) 218102644Snectar krb5_auth_con_free (context, s->ac); 219102644Snectar 220102644Snectar for (p = root; *p; p = &(*p)->next) 221102644Snectar if (*p == s) { 222102644Snectar *p = s->next; 223102644Snectar break; 224102644Snectar } 225102644Snectar free (s); 226102644Snectar} 227102644Snectar 228102644Snectarstatic void 229233294Sstasadd_slave (krb5_context context, krb5_keytab keytab, slave **root, 230233294Sstas krb5_socket_t fd) 23155682Smarkm{ 23255682Smarkm krb5_principal server; 23355682Smarkm krb5_error_code ret; 23455682Smarkm slave *s; 23572445Sassar socklen_t addr_len; 23655682Smarkm krb5_ticket *ticket = NULL; 23755682Smarkm char hostname[128]; 23855682Smarkm 23955682Smarkm s = malloc(sizeof(*s)); 24055682Smarkm if (s == NULL) { 24155682Smarkm krb5_warnx (context, "add_slave: no memory"); 24255682Smarkm return; 24355682Smarkm } 24455682Smarkm s->name = NULL; 24555682Smarkm s->ac = NULL; 24655682Smarkm 24755682Smarkm addr_len = sizeof(s->addr); 24855682Smarkm s->fd = accept (fd, (struct sockaddr *)&s->addr, &addr_len); 249233294Sstas if (rk_IS_BAD_SOCKET(s->fd)) { 250233294Sstas krb5_warn (context, rk_SOCK_ERRNO, "accept"); 25155682Smarkm goto error; 25255682Smarkm } 253178825Sdfr if (master_hostname) 254178825Sdfr strlcpy(hostname, master_hostname, sizeof(hostname)); 255178825Sdfr else 256178825Sdfr gethostname(hostname, sizeof(hostname)); 257178825Sdfr 25855682Smarkm ret = krb5_sname_to_principal (context, hostname, IPROP_NAME, 25955682Smarkm KRB5_NT_SRV_HST, &server); 26055682Smarkm if (ret) { 26155682Smarkm krb5_warn (context, ret, "krb5_sname_to_principal"); 26255682Smarkm goto error; 26355682Smarkm } 26455682Smarkm 26555682Smarkm ret = krb5_recvauth (context, &s->ac, &s->fd, 26672445Sassar IPROP_VERSION, server, 0, keytab, &ticket); 26755682Smarkm krb5_free_principal (context, server); 26855682Smarkm if (ret) { 26955682Smarkm krb5_warn (context, ret, "krb5_recvauth"); 27055682Smarkm goto error; 27155682Smarkm } 27255682Smarkm ret = krb5_unparse_name (context, ticket->client, &s->name); 273233294Sstas krb5_free_ticket (context, ticket); 27455682Smarkm if (ret) { 27555682Smarkm krb5_warn (context, ret, "krb5_unparse_name"); 27655682Smarkm goto error; 27755682Smarkm } 27855682Smarkm if (check_acl (context, s->name)) { 27955682Smarkm krb5_warnx (context, "%s not in acl", s->name); 28055682Smarkm goto error; 28155682Smarkm } 282102644Snectar 283102644Snectar { 284102644Snectar slave *l = *root; 285102644Snectar 286102644Snectar while (l) { 287102644Snectar if (strcmp(l->name, s->name) == 0) 288102644Snectar break; 289102644Snectar l = l->next; 290102644Snectar } 291102644Snectar if (l) { 292102644Snectar if (l->flags & SLAVE_F_DEAD) { 293102644Snectar remove_slave(context, l, root); 294102644Snectar } else { 295102644Snectar krb5_warnx (context, "second connection from %s", s->name); 296102644Snectar goto error; 297102644Snectar } 298102644Snectar } 299102644Snectar } 300102644Snectar 30172445Sassar krb5_warnx (context, "connection from %s", s->name); 30255682Smarkm 30355682Smarkm s->version = 0; 304102644Snectar s->flags = 0; 305102644Snectar slave_seen(s); 30655682Smarkm s->next = *root; 30755682Smarkm *root = s; 30855682Smarkm return; 30955682Smarkmerror: 310102644Snectar remove_slave(context, s, root); 31155682Smarkm} 31255682Smarkm 31372445Sassarstruct prop_context { 31472445Sassar krb5_auth_context auth_context; 315233294Sstas krb5_socket_t fd; 31672445Sassar}; 31772445Sassar 31855682Smarkmstatic int 319178825Sdfrprop_one (krb5_context context, HDB *db, hdb_entry_ex *entry, void *v) 32055682Smarkm{ 32172445Sassar krb5_error_code ret; 322178825Sdfr krb5_storage *sp; 32372445Sassar krb5_data data; 324178825Sdfr struct slave *s = (struct slave *)v; 32572445Sassar 326178825Sdfr ret = hdb_entry2value (context, &entry->entry, &data); 32772445Sassar if (ret) 32872445Sassar return ret; 32972445Sassar ret = krb5_data_realloc (&data, data.length + 4); 33072445Sassar if (ret) { 33172445Sassar krb5_data_free (&data); 33272445Sassar return ret; 33372445Sassar } 33472445Sassar memmove ((char *)data.data + 4, data.data, data.length - 4); 335178825Sdfr sp = krb5_storage_from_data(&data); 336178825Sdfr if (sp == NULL) { 337178825Sdfr krb5_data_free (&data); 338178825Sdfr return ENOMEM; 339178825Sdfr } 340178825Sdfr krb5_store_int32(sp, ONE_PRINC); 341178825Sdfr krb5_storage_free(sp); 34272445Sassar 343178825Sdfr ret = krb5_write_priv_message (context, s->ac, &s->fd, &data); 34472445Sassar krb5_data_free (&data); 34572445Sassar return ret; 34655682Smarkm} 34755682Smarkm 34855682Smarkmstatic int 34972445Sassarsend_complete (krb5_context context, slave *s, 350178825Sdfr const char *database, uint32_t current_version) 35172445Sassar{ 35272445Sassar krb5_error_code ret; 353178825Sdfr krb5_storage *sp; 35472445Sassar HDB *db; 35572445Sassar krb5_data data; 35672445Sassar char buf[8]; 35772445Sassar 35872445Sassar ret = hdb_create (context, &db, database); 35972445Sassar if (ret) 36072445Sassar krb5_err (context, 1, ret, "hdb_create: %s", database); 361178825Sdfr ret = db->hdb_open (context, db, O_RDONLY, 0); 36272445Sassar if (ret) 36372445Sassar krb5_err (context, 1, ret, "db->open"); 36472445Sassar 365178825Sdfr sp = krb5_storage_from_mem (buf, 4); 366178825Sdfr if (sp == NULL) 367178825Sdfr krb5_errx (context, 1, "krb5_storage_from_mem"); 368178825Sdfr krb5_store_int32 (sp, TELL_YOU_EVERYTHING); 369178825Sdfr krb5_storage_free (sp); 37072445Sassar 37172445Sassar data.data = buf; 37272445Sassar data.length = 4; 37372445Sassar 37472445Sassar ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); 37572445Sassar 376102644Snectar if (ret) { 377102644Snectar krb5_warn (context, ret, "krb5_write_priv_message"); 378178825Sdfr slave_dead(context, s); 379102644Snectar return ret; 380102644Snectar } 38172445Sassar 382233294Sstas ret = hdb_foreach (context, db, HDB_F_ADMIN_DATA, prop_one, s); 383102644Snectar if (ret) { 384102644Snectar krb5_warn (context, ret, "hdb_foreach"); 385178825Sdfr slave_dead(context, s); 386102644Snectar return ret; 387102644Snectar } 38872445Sassar 389178825Sdfr (*db->hdb_close)(context, db); 390178825Sdfr (*db->hdb_destroy)(context, db); 391178825Sdfr 392178825Sdfr sp = krb5_storage_from_mem (buf, 8); 393178825Sdfr if (sp == NULL) 394178825Sdfr krb5_errx (context, 1, "krb5_storage_from_mem"); 395178825Sdfr krb5_store_int32 (sp, NOW_YOU_HAVE); 396178825Sdfr krb5_store_int32 (sp, current_version); 397178825Sdfr krb5_storage_free (sp); 398178825Sdfr 39972445Sassar data.length = 8; 40072445Sassar 401102644Snectar s->version = current_version; 402102644Snectar 40372445Sassar ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); 404102644Snectar if (ret) { 405178825Sdfr slave_dead(context, s); 406102644Snectar krb5_warn (context, ret, "krb5_write_priv_message"); 407102644Snectar return ret; 408102644Snectar } 40972445Sassar 410102644Snectar slave_seen(s); 41172445Sassar 41272445Sassar return 0; 41372445Sassar} 41472445Sassar 41572445Sassarstatic int 416178825Sdfrsend_are_you_there (krb5_context context, slave *s) 417178825Sdfr{ 418178825Sdfr krb5_storage *sp; 419178825Sdfr krb5_data data; 420178825Sdfr char buf[4]; 421178825Sdfr int ret; 422178825Sdfr 423178825Sdfr if (s->flags & (SLAVE_F_DEAD|SLAVE_F_AYT)) 424178825Sdfr return 0; 425178825Sdfr 426233294Sstas krb5_warnx(context, "slave %s missing, sending AYT", s->name); 427233294Sstas 428178825Sdfr s->flags |= SLAVE_F_AYT; 429178825Sdfr 430178825Sdfr data.data = buf; 431178825Sdfr data.length = 4; 432178825Sdfr 433178825Sdfr sp = krb5_storage_from_mem (buf, 4); 434178825Sdfr if (sp == NULL) { 435178825Sdfr krb5_warnx (context, "are_you_there: krb5_data_alloc"); 436178825Sdfr slave_dead(context, s); 437178825Sdfr return 1; 438178825Sdfr } 439178825Sdfr krb5_store_int32 (sp, ARE_YOU_THERE); 440178825Sdfr krb5_storage_free (sp); 441178825Sdfr 442178825Sdfr ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); 443178825Sdfr 444178825Sdfr if (ret) { 445178825Sdfr krb5_warn (context, ret, "are_you_there: krb5_write_priv_message"); 446178825Sdfr slave_dead(context, s); 447178825Sdfr return 1; 448178825Sdfr } 449178825Sdfr 450178825Sdfr return 0; 451178825Sdfr} 452178825Sdfr 453178825Sdfrstatic int 45455682Smarkmsend_diffs (krb5_context context, slave *s, int log_fd, 455178825Sdfr const char *database, uint32_t current_version) 45655682Smarkm{ 45772445Sassar krb5_storage *sp; 458178825Sdfr uint32_t ver; 45955682Smarkm time_t timestamp; 46055682Smarkm enum kadm_ops op; 461178825Sdfr uint32_t len; 46255682Smarkm off_t right, left; 46355682Smarkm krb5_data data; 46455682Smarkm int ret = 0; 46555682Smarkm 466178825Sdfr if (s->version == current_version) { 467178825Sdfr krb5_warnx(context, "slave %s in sync already at version %ld", 468178825Sdfr s->name, (long)s->version); 46955682Smarkm return 0; 470178825Sdfr } 47155682Smarkm 472102644Snectar if (s->flags & SLAVE_F_DEAD) 473102644Snectar return 0; 474102644Snectar 475178825Sdfr /* if slave is a fresh client, starting over */ 476178825Sdfr if (s->version == 0) { 477178825Sdfr krb5_warnx(context, "sending complete log to fresh slave %s", 478178825Sdfr s->name); 479178825Sdfr return send_complete (context, s, database, current_version); 480178825Sdfr } 481178825Sdfr 48255682Smarkm sp = kadm5_log_goto_end (log_fd); 483102644Snectar right = krb5_storage_seek(sp, 0, SEEK_CUR); 48455682Smarkm for (;;) { 485178825Sdfr ret = kadm5_log_previous (context, sp, &ver, ×tamp, &op, &len); 486178825Sdfr if (ret) 487233294Sstas krb5_err(context, 1, ret, 488178825Sdfr "send_diffs: failed to find previous entry"); 489102644Snectar left = krb5_storage_seek(sp, -16, SEEK_CUR); 49055682Smarkm if (ver == s->version) 49155682Smarkm return 0; 49255682Smarkm if (ver == s->version + 1) 49355682Smarkm break; 494178825Sdfr if (left == 0) { 495233294Sstas krb5_storage_free(sp); 496178825Sdfr krb5_warnx(context, 497178825Sdfr "slave %s (version %lu) out of sync with master " 498178825Sdfr "(first version in log %lu), sending complete database", 499178825Sdfr s->name, (unsigned long)s->version, (unsigned long)ver); 50072445Sassar return send_complete (context, s, database, current_version); 501178825Sdfr } 50255682Smarkm } 503178825Sdfr 504178825Sdfr krb5_warnx(context, 505178825Sdfr "syncing slave %s from version %lu to version %lu", 506178825Sdfr s->name, (unsigned long)s->version, 507178825Sdfr (unsigned long)current_version); 508178825Sdfr 509178825Sdfr ret = krb5_data_alloc (&data, right - left + 4); 510178825Sdfr if (ret) { 511233294Sstas krb5_storage_free(sp); 512178825Sdfr krb5_warn (context, ret, "send_diffs: krb5_data_alloc"); 513178825Sdfr slave_dead(context, s); 514178825Sdfr return 1; 515178825Sdfr } 516102644Snectar krb5_storage_read (sp, (char *)data.data + 4, data.length - 4); 51755682Smarkm krb5_storage_free(sp); 51855682Smarkm 519178825Sdfr sp = krb5_storage_from_data (&data); 520178825Sdfr if (sp == NULL) { 521178825Sdfr krb5_warnx (context, "send_diffs: krb5_storage_from_data"); 522178825Sdfr slave_dead(context, s); 523178825Sdfr return 1; 524178825Sdfr } 525178825Sdfr krb5_store_int32 (sp, FOR_YOU); 526178825Sdfr krb5_storage_free(sp); 52755682Smarkm 52872445Sassar ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); 529102644Snectar krb5_data_free(&data); 53055682Smarkm 53155682Smarkm if (ret) { 532178825Sdfr krb5_warn (context, ret, "send_diffs: krb5_write_priv_message"); 533178825Sdfr slave_dead(context, s); 53455682Smarkm return 1; 53555682Smarkm } 536102644Snectar slave_seen(s); 537102644Snectar 538178825Sdfr s->version = current_version; 539178825Sdfr 54055682Smarkm return 0; 54155682Smarkm} 54255682Smarkm 54355682Smarkmstatic int 54455682Smarkmprocess_msg (krb5_context context, slave *s, int log_fd, 545178825Sdfr const char *database, uint32_t current_version) 54655682Smarkm{ 54755682Smarkm int ret = 0; 54872445Sassar krb5_data out; 54955682Smarkm krb5_storage *sp; 55055682Smarkm int32_t tmp; 55155682Smarkm 55272445Sassar ret = krb5_read_priv_message(context, s->ac, &s->fd, &out); 55372445Sassar if(ret) { 55472445Sassar krb5_warn (context, ret, "error reading message from %s", s->name); 55555682Smarkm return 1; 55655682Smarkm } 55755682Smarkm 55855682Smarkm sp = krb5_storage_from_mem (out.data, out.length); 559178825Sdfr if (sp == NULL) { 560178825Sdfr krb5_warnx (context, "process_msg: no memory"); 561178825Sdfr krb5_data_free (&out); 562178825Sdfr return 1; 563178825Sdfr } 564178825Sdfr if (krb5_ret_int32 (sp, &tmp) != 0) { 565178825Sdfr krb5_warnx (context, "process_msg: client send too short command"); 566178825Sdfr krb5_data_free (&out); 567178825Sdfr return 1; 568178825Sdfr } 56955682Smarkm switch (tmp) { 57055682Smarkm case I_HAVE : 571178825Sdfr ret = krb5_ret_int32 (sp, &tmp); 572178825Sdfr if (ret != 0) { 573178825Sdfr krb5_warnx (context, "process_msg: client send too I_HAVE data"); 574178825Sdfr break; 575178825Sdfr } 576178825Sdfr /* new started slave that have old log */ 577178825Sdfr if (s->version == 0 && tmp != 0) { 578233294Sstas if (current_version < (uint32_t)tmp) { 579233294Sstas krb5_warnx (context, "Slave %s (version %lu) have later version " 580233294Sstas "the master (version %lu) OUT OF SYNC", 581233294Sstas s->name, (unsigned long)tmp, 582233294Sstas (unsigned long)current_version); 583178825Sdfr } 584233294Sstas s->version = tmp; 585178825Sdfr } 586233294Sstas if ((uint32_t)tmp < s->version) { 587178825Sdfr krb5_warnx (context, "Slave claims to not have " 588178825Sdfr "version we already sent to it"); 589178825Sdfr } else { 590178825Sdfr ret = send_diffs (context, s, log_fd, database, current_version); 591178825Sdfr } 59255682Smarkm break; 593178825Sdfr case I_AM_HERE : 594178825Sdfr break; 595178825Sdfr case ARE_YOU_THERE: 59655682Smarkm case FOR_YOU : 59755682Smarkm default : 59855682Smarkm krb5_warnx (context, "Ignoring command %d", tmp); 59955682Smarkm break; 60055682Smarkm } 60155682Smarkm 60255682Smarkm krb5_data_free (&out); 603233294Sstas krb5_storage_free (sp); 604102644Snectar 605102644Snectar slave_seen(s); 606102644Snectar 60755682Smarkm return ret; 60855682Smarkm} 60955682Smarkm 610102644Snectar#define SLAVE_NAME "Name" 611102644Snectar#define SLAVE_ADDRESS "Address" 612102644Snectar#define SLAVE_VERSION "Version" 613102644Snectar#define SLAVE_STATUS "Status" 614102644Snectar#define SLAVE_SEEN "Last Seen" 615102644Snectar 616178825Sdfrstatic FILE * 617178825Sdfropen_stats(krb5_context context) 618178825Sdfr{ 619178825Sdfr char *statfile = NULL; 620178825Sdfr const char *fn; 621178825Sdfr FILE *f; 622178825Sdfr 623178825Sdfr if (slave_stats_file) 624178825Sdfr fn = slave_stats_file; 625178825Sdfr else { 626178825Sdfr asprintf(&statfile, "%s/slaves-stats", hdb_db_dir(context)); 627178825Sdfr fn = krb5_config_get_string_default(context, 628178825Sdfr NULL, 629178825Sdfr statfile, 630178825Sdfr "kdc", 631178825Sdfr "iprop-stats", 632178825Sdfr NULL); 633178825Sdfr } 634178825Sdfr f = fopen(fn, "w"); 635178825Sdfr if (statfile) 636178825Sdfr free(statfile); 637178825Sdfr 638178825Sdfr return f; 639178825Sdfr} 640178825Sdfr 641102644Snectarstatic void 642178825Sdfrwrite_master_down(krb5_context context) 643102644Snectar{ 644120945Snectar char str[100]; 645178825Sdfr time_t t = time(NULL); 646178825Sdfr FILE *fp; 647178825Sdfr 648178825Sdfr fp = open_stats(context); 649178825Sdfr if (fp == NULL) 650178825Sdfr return; 651233294Sstas krb5_format_time(context, t, str, sizeof(str), TRUE); 652178825Sdfr fprintf(fp, "master down at %s\n", str); 653178825Sdfr 654178825Sdfr fclose(fp); 655178825Sdfr} 656178825Sdfr 657178825Sdfrstatic void 658178825Sdfrwrite_stats(krb5_context context, slave *slaves, uint32_t current_version) 659178825Sdfr{ 660178825Sdfr char str[100]; 661102644Snectar rtbl_t tbl; 662102644Snectar time_t t = time(NULL); 663102644Snectar FILE *fp; 664102644Snectar 665178825Sdfr fp = open_stats(context); 666102644Snectar if (fp == NULL) 667102644Snectar return; 668102644Snectar 669233294Sstas krb5_format_time(context, t, str, sizeof(str), TRUE); 670102644Snectar fprintf(fp, "Status for slaves, last updated: %s\n\n", str); 671102644Snectar 672102644Snectar fprintf(fp, "Master version: %lu\n\n", (unsigned long)current_version); 673102644Snectar 674102644Snectar tbl = rtbl_create(); 675102644Snectar if (tbl == NULL) { 676102644Snectar fclose(fp); 677102644Snectar return; 678102644Snectar } 679102644Snectar 680102644Snectar rtbl_add_column(tbl, SLAVE_NAME, 0); 681102644Snectar rtbl_add_column(tbl, SLAVE_ADDRESS, 0); 682102644Snectar rtbl_add_column(tbl, SLAVE_VERSION, RTBL_ALIGN_RIGHT); 683102644Snectar rtbl_add_column(tbl, SLAVE_STATUS, 0); 684102644Snectar rtbl_add_column(tbl, SLAVE_SEEN, 0); 685102644Snectar 686102644Snectar rtbl_set_prefix(tbl, " "); 687102644Snectar rtbl_set_column_prefix(tbl, SLAVE_NAME, ""); 688102644Snectar 689102644Snectar while (slaves) { 690102644Snectar krb5_address addr; 691102644Snectar krb5_error_code ret; 692102644Snectar rtbl_add_column_entry(tbl, SLAVE_NAME, slaves->name); 693233294Sstas ret = krb5_sockaddr2address (context, 694102644Snectar (struct sockaddr*)&slaves->addr, &addr); 695102644Snectar if(ret == 0) { 696102644Snectar krb5_print_address(&addr, str, sizeof(str), NULL); 697102644Snectar krb5_free_address(context, &addr); 698102644Snectar rtbl_add_column_entry(tbl, SLAVE_ADDRESS, str); 699102644Snectar } else 700102644Snectar rtbl_add_column_entry(tbl, SLAVE_ADDRESS, "<unknown>"); 701233294Sstas 702102644Snectar snprintf(str, sizeof(str), "%u", (unsigned)slaves->version); 703102644Snectar rtbl_add_column_entry(tbl, SLAVE_VERSION, str); 704102644Snectar 705102644Snectar if (slaves->flags & SLAVE_F_DEAD) 706102644Snectar rtbl_add_column_entry(tbl, SLAVE_STATUS, "Down"); 707102644Snectar else 708102644Snectar rtbl_add_column_entry(tbl, SLAVE_STATUS, "Up"); 709102644Snectar 710233294Sstas ret = krb5_format_time(context, slaves->seen, str, sizeof(str), TRUE); 711102644Snectar rtbl_add_column_entry(tbl, SLAVE_SEEN, str); 712102644Snectar 713102644Snectar slaves = slaves->next; 714102644Snectar } 715102644Snectar 716102644Snectar rtbl_format(tbl, fp); 717102644Snectar rtbl_destroy(tbl); 718102644Snectar 719102644Snectar fclose(fp); 720102644Snectar} 721102644Snectar 722102644Snectar 723233294Sstasstatic char sHDB[] = "HDB:"; 72472445Sassarstatic char *realm; 72572445Sassarstatic int version_flag; 72672445Sassarstatic int help_flag; 727233294Sstasstatic char *keytab_str = sHDB; 72872445Sassarstatic char *database; 729178825Sdfrstatic char *config_file; 730178825Sdfrstatic char *port_str; 731233294Sstas#ifdef SUPPORT_DETACH 732178825Sdfrstatic int detach_from_console = 0; 733233294Sstas#endif 73472445Sassar 73572445Sassarstatic struct getargs args[] = { 736233294Sstas { "config-file", 'c', arg_string, &config_file, NULL, NULL }, 737233294Sstas { "realm", 'r', arg_string, &realm, NULL, NULL }, 73872445Sassar { "keytab", 'k', arg_string, &keytab_str, 73972445Sassar "keytab to get authentication from", "kspec" }, 74072445Sassar { "database", 'd', arg_string, &database, "database", "file"}, 741233294Sstas { "slave-stats-file", 0, arg_string, rk_UNCONST(&slave_stats_file), 742178825Sdfr "file for slave status information", "file"}, 743233294Sstas { "time-missing", 0, arg_string, rk_UNCONST(&slave_time_missing), 744178825Sdfr "time before slave is polled for presence", "time"}, 745233294Sstas { "time-gone", 0, arg_string, rk_UNCONST(&slave_time_gone), 746178825Sdfr "time of inactivity after which a slave is considered gone", "time"}, 747178825Sdfr { "port", 0, arg_string, &port_str, 748178825Sdfr "port ipropd will listen to", "port"}, 749233294Sstas#ifdef SUPPORT_DETACH 750233294Sstas { "detach", 0, arg_flag, &detach_from_console, 751233294Sstas "detach from console", NULL }, 752233294Sstas#endif 753233294Sstas { "hostname", 0, arg_string, rk_UNCONST(&master_hostname), 754178825Sdfr "hostname of master (if not same as hostname)", "hostname" }, 755233294Sstas { "version", 0, arg_flag, &version_flag, NULL, NULL }, 756233294Sstas { "help", 0, arg_flag, &help_flag, NULL, NULL } 75755682Smarkm}; 75872445Sassarstatic int num_args = sizeof(args) / sizeof(args[0]); 75955682Smarkm 76055682Smarkmint 76155682Smarkmmain(int argc, char **argv) 76255682Smarkm{ 76355682Smarkm krb5_error_code ret; 76455682Smarkm krb5_context context; 76555682Smarkm void *kadm_handle; 76655682Smarkm kadm5_server_context *server_context; 76755682Smarkm kadm5_config_params conf; 768233294Sstas krb5_socket_t signal_fd, listen_fd; 76955682Smarkm int log_fd; 77055682Smarkm slave *slaves = NULL; 771178825Sdfr uint32_t current_version = 0, old_version = 0; 77272445Sassar krb5_keytab keytab; 773178825Sdfr int optidx; 774178825Sdfr char **files; 775233294Sstas 776178825Sdfr optidx = krb5_program_setup(&context, argc, argv, args, num_args, NULL); 777233294Sstas 77855682Smarkm if(help_flag) 77955682Smarkm krb5_std_usage(0, args, num_args); 78055682Smarkm if(version_flag) { 78155682Smarkm print_version(NULL); 78255682Smarkm exit(0); 78355682Smarkm } 78455682Smarkm 785178825Sdfr setup_signal(); 786178825Sdfr 787178825Sdfr if (config_file == NULL) { 788178825Sdfr asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context)); 789178825Sdfr if (config_file == NULL) 790178825Sdfr errx(1, "out of memory"); 791178825Sdfr } 792178825Sdfr 793178825Sdfr ret = krb5_prepend_config_files_default(config_file, &files); 794178825Sdfr if (ret) 795178825Sdfr krb5_err(context, 1, ret, "getting configuration files"); 796178825Sdfr 797178825Sdfr ret = krb5_set_config_files(context, files); 798178825Sdfr krb5_free_config_files(files); 799178825Sdfr if (ret) 800178825Sdfr krb5_err(context, 1, ret, "reading configuration files"); 801178825Sdfr 802178825Sdfr time_before_gone = parse_time (slave_time_gone, "s"); 803178825Sdfr if (time_before_gone < 0) 804178825Sdfr krb5_errx (context, 1, "couldn't parse time: %s", slave_time_gone); 805178825Sdfr time_before_missing = parse_time (slave_time_missing, "s"); 806178825Sdfr if (time_before_missing < 0) 807178825Sdfr krb5_errx (context, 1, "couldn't parse time: %s", slave_time_missing); 808178825Sdfr 809233294Sstas#ifdef SUPPORT_DETACH 810178825Sdfr if (detach_from_console) 811178825Sdfr daemon(0, 0); 812233294Sstas#endif 81390926Snectar pidfile (NULL); 81472445Sassar krb5_openlog (context, "ipropd-master", &log_facility); 81572445Sassar krb5_set_warn_dest(context, log_facility); 81672445Sassar 81772445Sassar ret = krb5_kt_register(context, &hdb_kt_ops); 81872445Sassar if(ret) 81972445Sassar krb5_err(context, 1, ret, "krb5_kt_register"); 82072445Sassar 82172445Sassar ret = krb5_kt_resolve(context, keytab_str, &keytab); 82272445Sassar if(ret) 82372445Sassar krb5_err(context, 1, ret, "krb5_kt_resolve: %s", keytab_str); 824233294Sstas 82555682Smarkm memset(&conf, 0, sizeof(conf)); 82655682Smarkm if(realm) { 82755682Smarkm conf.mask |= KADM5_CONFIG_REALM; 82855682Smarkm conf.realm = realm; 82955682Smarkm } 83072445Sassar ret = kadm5_init_with_skey_ctx (context, 83172445Sassar KADM5_ADMIN_SERVICE, 83272445Sassar NULL, 83372445Sassar KADM5_ADMIN_SERVICE, 834233294Sstas &conf, 0, 0, 83572445Sassar &kadm_handle); 83655682Smarkm if (ret) 83755682Smarkm krb5_err (context, 1, ret, "kadm5_init_with_password_ctx"); 83855682Smarkm 83955682Smarkm server_context = (kadm5_server_context *)kadm_handle; 84055682Smarkm 84155682Smarkm log_fd = open (server_context->log_context.log_file, O_RDONLY, 0); 84255682Smarkm if (log_fd < 0) 84355682Smarkm krb5_err (context, 1, errno, "open %s", 84455682Smarkm server_context->log_context.log_file); 84555682Smarkm 84655682Smarkm signal_fd = make_signal_socket (context); 847178825Sdfr listen_fd = make_listen_socket (context, port_str); 84855682Smarkm 849178825Sdfr kadm5_log_get_version_fd (log_fd, ¤t_version); 85072445Sassar 851233294Sstas krb5_warnx(context, "ipropd-master started at version: %lu", 852178825Sdfr (unsigned long)current_version); 853178825Sdfr 854178825Sdfr while(exit_flag == 0){ 85555682Smarkm slave *p; 85655682Smarkm fd_set readset; 85755682Smarkm int max_fd = 0; 85855682Smarkm struct timeval to = {30, 0}; 859178825Sdfr uint32_t vers; 86055682Smarkm 861233294Sstas#ifndef NO_LIMIT_FD_SETSIZE 86272445Sassar if (signal_fd >= FD_SETSIZE || listen_fd >= FD_SETSIZE) 86372445Sassar krb5_errx (context, 1, "fd too large"); 864233294Sstas#endif 86572445Sassar 86655682Smarkm FD_ZERO(&readset); 86755682Smarkm FD_SET(signal_fd, &readset); 86855682Smarkm max_fd = max(max_fd, signal_fd); 86955682Smarkm FD_SET(listen_fd, &readset); 87055682Smarkm max_fd = max(max_fd, listen_fd); 87155682Smarkm 87255682Smarkm for (p = slaves; p != NULL; p = p->next) { 873120945Snectar if (p->flags & SLAVE_F_DEAD) 874120945Snectar continue; 87555682Smarkm FD_SET(p->fd, &readset); 87655682Smarkm max_fd = max(max_fd, p->fd); 87755682Smarkm } 87855682Smarkm 87955682Smarkm ret = select (max_fd + 1, 88055682Smarkm &readset, NULL, NULL, &to); 88155682Smarkm if (ret < 0) { 88255682Smarkm if (errno == EINTR) 88355682Smarkm continue; 88455682Smarkm else 88555682Smarkm krb5_err (context, 1, errno, "select"); 88655682Smarkm } 88755682Smarkm 88855682Smarkm if (ret == 0) { 88955682Smarkm old_version = current_version; 89072445Sassar kadm5_log_get_version_fd (log_fd, ¤t_version); 89155682Smarkm 892178825Sdfr if (current_version > old_version) { 893233294Sstas krb5_warnx(context, 894178825Sdfr "Missed a signal, updating slaves %lu to %lu", 895178825Sdfr (unsigned long)old_version, 896178825Sdfr (unsigned long)current_version); 897120945Snectar for (p = slaves; p != NULL; p = p->next) { 898120945Snectar if (p->flags & SLAVE_F_DEAD) 899120945Snectar continue; 90072445Sassar send_diffs (context, p, log_fd, database, current_version); 901120945Snectar } 902178825Sdfr } 90355682Smarkm } 90455682Smarkm 90555682Smarkm if (ret && FD_ISSET(signal_fd, &readset)) { 906233294Sstas#ifndef NO_UNIX_SOCKETS 90755682Smarkm struct sockaddr_un peer_addr; 908233294Sstas#else 909233294Sstas struct sockaddr_storage peer_addr; 910233294Sstas#endif 91172445Sassar socklen_t peer_len = sizeof(peer_addr); 91255682Smarkm 91390926Snectar if(recvfrom(signal_fd, (void *)&vers, sizeof(vers), 0, 91455682Smarkm (struct sockaddr *)&peer_addr, &peer_len) < 0) { 91555682Smarkm krb5_warn (context, errno, "recvfrom"); 91655682Smarkm continue; 91755682Smarkm } 91855682Smarkm --ret; 919178825Sdfr assert(ret >= 0); 92055682Smarkm old_version = current_version; 92172445Sassar kadm5_log_get_version_fd (log_fd, ¤t_version); 922178825Sdfr if (current_version > old_version) { 923233294Sstas krb5_warnx(context, 924178825Sdfr "Got a signal, updating slaves %lu to %lu", 925178825Sdfr (unsigned long)old_version, 926178825Sdfr (unsigned long)current_version); 927233294Sstas for (p = slaves; p != NULL; p = p->next) { 928233294Sstas if (p->flags & SLAVE_F_DEAD) 929233294Sstas continue; 930178825Sdfr send_diffs (context, p, log_fd, database, current_version); 931233294Sstas } 932178825Sdfr } else { 933233294Sstas krb5_warnx(context, 934178825Sdfr "Got a signal, but no update in log version %lu", 935178825Sdfr (unsigned long)current_version); 936178825Sdfr } 937178825Sdfr } 93855682Smarkm 939178825Sdfr for(p = slaves; p != NULL; p = p->next) { 940120945Snectar if (p->flags & SLAVE_F_DEAD) 941178825Sdfr continue; 942178825Sdfr if (ret && FD_ISSET(p->fd, &readset)) { 94378527Sassar --ret; 944178825Sdfr assert(ret >= 0); 94572445Sassar if(process_msg (context, p, log_fd, database, current_version)) 946178825Sdfr slave_dead(context, p); 947178825Sdfr } else if (slave_gone_p (p)) 948178825Sdfr slave_dead(context, p); 949233294Sstas else if (slave_missing_p (p)) 950178825Sdfr send_are_you_there (context, p); 951120945Snectar } 95255682Smarkm 95355682Smarkm if (ret && FD_ISSET(listen_fd, &readset)) { 95472445Sassar add_slave (context, keytab, &slaves, listen_fd); 95555682Smarkm --ret; 956178825Sdfr assert(ret >= 0); 95755682Smarkm } 958102644Snectar write_stats(context, slaves, current_version); 95955682Smarkm } 96055682Smarkm 961233294Sstas if(exit_flag == SIGINT || exit_flag == SIGTERM) 962233294Sstas krb5_warnx(context, "%s terminated", getprogname()); 963233294Sstas#ifdef SIGXCPU 964233294Sstas else if(exit_flag == SIGXCPU) 965178825Sdfr krb5_warnx(context, "%s CPU time limit exceeded", getprogname()); 966233294Sstas#endif 967178825Sdfr else 968233294Sstas krb5_warnx(context, "%s unexpected exit reason: %ld", 969233294Sstas getprogname(), (long)exit_flag); 970178825Sdfr 971178825Sdfr write_master_down(context); 972178825Sdfr 97355682Smarkm return 0; 97455682Smarkm} 975