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" 3555682Smarkm 36233294SstasRCSID("$Id$"); 3755682Smarkm 38233294Sstasstatic const char *config_name = "ipropd-slave"; 39233294Sstas 4072445Sassarstatic krb5_log_facility *log_facility; 41233294Sstasstatic char five_min[] = "5 min"; 42233294Sstasstatic char *server_time_lost = five_min; 43178825Sdfrstatic int time_before_lost; 44178825Sdfrconst char *slave_str = NULL; 4572445Sassar 4655682Smarkmstatic int 47178825Sdfrconnect_to_master (krb5_context context, const char *master, 48178825Sdfr const char *port_str) 4955682Smarkm{ 50233294Sstas char port[NI_MAXSERV]; 51233294Sstas struct addrinfo *ai, *a; 52233294Sstas struct addrinfo hints; 53233294Sstas int error; 54233294Sstas int s = -1; 5555682Smarkm 56233294Sstas memset (&hints, 0, sizeof(hints)); 57233294Sstas hints.ai_socktype = SOCK_STREAM; 58233294Sstas 59233294Sstas if (port_str == NULL) { 60233294Sstas snprintf(port, sizeof(port), "%u", IPROP_PORT); 61233294Sstas port_str = port; 62233294Sstas } 63233294Sstas 64233294Sstas error = getaddrinfo (master, port_str, &hints, &ai); 65233294Sstas if (error) { 66233294Sstas krb5_warnx(context, "Failed to get address of to %s: %s", 67233294Sstas master, gai_strerror(error)); 68233294Sstas return -1; 69233294Sstas } 70233294Sstas 71233294Sstas for (a = ai; a != NULL; a = a->ai_next) { 72233294Sstas char node[NI_MAXHOST]; 73233294Sstas error = getnameinfo(a->ai_addr, a->ai_addrlen, 74233294Sstas node, sizeof(node), NULL, 0, NI_NUMERICHOST); 75233294Sstas if (error) 76233294Sstas strlcpy(node, "[unknown-addr]", sizeof(node)); 77233294Sstas 78233294Sstas s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 79233294Sstas if (s < 0) 80233294Sstas continue; 81233294Sstas if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 82233294Sstas krb5_warn(context, errno, "connection failed to %s[%s]", 83233294Sstas master, node); 84233294Sstas close (s); 85233294Sstas continue; 86178825Sdfr } 87233294Sstas krb5_warnx(context, "connection successful " 88233294Sstas "to master: %s[%s]", master, node); 89233294Sstas break; 90178825Sdfr } 91233294Sstas freeaddrinfo (ai); 92233294Sstas 93233294Sstas if (a == NULL) 94233294Sstas return -1; 95233294Sstas 96233294Sstas return s; 9755682Smarkm} 9855682Smarkm 9955682Smarkmstatic void 10072445Sassarget_creds(krb5_context context, const char *keytab_str, 101178825Sdfr krb5_ccache *cache, const char *serverhost) 10255682Smarkm{ 10355682Smarkm krb5_keytab keytab; 10455682Smarkm krb5_principal client; 10555682Smarkm krb5_error_code ret; 106178825Sdfr krb5_get_init_creds_opt *init_opts; 10755682Smarkm krb5_creds creds; 10855682Smarkm char *server; 10972445Sassar char keytab_buf[256]; 110233294Sstas 11172445Sassar if (keytab_str == NULL) { 11272445Sassar ret = krb5_kt_default_name (context, keytab_buf, sizeof(keytab_buf)); 11372445Sassar if (ret) 11472445Sassar krb5_err (context, 1, ret, "krb5_kt_default_name"); 11572445Sassar keytab_str = keytab_buf; 11672445Sassar } 11772445Sassar 11872445Sassar ret = krb5_kt_resolve(context, keytab_str, &keytab); 11972445Sassar if(ret) 12072445Sassar krb5_err(context, 1, ret, "%s", keytab_str); 121178825Sdfr 122233294Sstas 123178825Sdfr ret = krb5_sname_to_principal (context, slave_str, IPROP_NAME, 12455682Smarkm KRB5_NT_SRV_HST, &client); 12555682Smarkm if (ret) krb5_err(context, 1, ret, "krb5_sname_to_principal"); 12655682Smarkm 127178825Sdfr ret = krb5_get_init_creds_opt_alloc(context, &init_opts); 128178825Sdfr if (ret) krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc"); 12955682Smarkm 130178825Sdfr asprintf (&server, "%s/%s", IPROP_NAME, serverhost); 13155682Smarkm if (server == NULL) 13255682Smarkm krb5_errx (context, 1, "malloc: no memory"); 13355682Smarkm 13455682Smarkm ret = krb5_get_init_creds_keytab(context, &creds, client, keytab, 135178825Sdfr 0, server, init_opts); 13655682Smarkm free (server); 137178825Sdfr krb5_get_init_creds_opt_free(context, init_opts); 13855682Smarkm if(ret) krb5_err(context, 1, ret, "krb5_get_init_creds"); 139233294Sstas 14055682Smarkm ret = krb5_kt_close(context, keytab); 14155682Smarkm if(ret) krb5_err(context, 1, ret, "krb5_kt_close"); 14255682Smarkm 143233294Sstas ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, cache); 144233294Sstas if(ret) krb5_err(context, 1, ret, "krb5_cc_new_unique"); 145233294Sstas 14655682Smarkm ret = krb5_cc_initialize(context, *cache, client); 14755682Smarkm if(ret) krb5_err(context, 1, ret, "krb5_cc_initialize"); 14855682Smarkm 14955682Smarkm ret = krb5_cc_store_cred(context, *cache, &creds); 15055682Smarkm if(ret) krb5_err(context, 1, ret, "krb5_cc_store_cred"); 151233294Sstas 152233294Sstas krb5_free_cred_contents(context, &creds); 153233294Sstas krb5_free_principal(context, client); 15455682Smarkm} 15555682Smarkm 156233294Sstasstatic krb5_error_code 15755682Smarkmihave (krb5_context context, krb5_auth_context auth_context, 158178825Sdfr int fd, uint32_t version) 15955682Smarkm{ 16055682Smarkm int ret; 16155682Smarkm u_char buf[8]; 16255682Smarkm krb5_storage *sp; 163178825Sdfr krb5_data data; 16455682Smarkm 16555682Smarkm sp = krb5_storage_from_mem (buf, 8); 16655682Smarkm krb5_store_int32 (sp, I_HAVE); 16755682Smarkm krb5_store_int32 (sp, version); 16855682Smarkm krb5_storage_free (sp); 16955682Smarkm data.length = 8; 17055682Smarkm data.data = buf; 171233294Sstas 172178825Sdfr ret = krb5_write_priv_message(context, auth_context, &fd, &data); 17355682Smarkm if (ret) 174233294Sstas krb5_warn (context, ret, "krb5_write_message"); 175233294Sstas return ret; 17655682Smarkm} 17755682Smarkm 17855682Smarkmstatic void 17972445Sassarreceive_loop (krb5_context context, 18072445Sassar krb5_storage *sp, 18172445Sassar kadm5_server_context *server_context) 18255682Smarkm{ 18355682Smarkm int ret; 18455682Smarkm off_t left, right; 18555682Smarkm void *buf; 186178825Sdfr int32_t vers, vers2; 187178825Sdfr ssize_t sret; 18855682Smarkm 189178825Sdfr /* 190178825Sdfr * Seek to the current version of the local database. 191178825Sdfr */ 19255682Smarkm do { 19355682Smarkm int32_t len, timestamp, tmp; 19455682Smarkm enum kadm_ops op; 19555682Smarkm 19655682Smarkm if(krb5_ret_int32 (sp, &vers) != 0) 19755682Smarkm return; 19855682Smarkm krb5_ret_int32 (sp, ×tamp); 19955682Smarkm krb5_ret_int32 (sp, &tmp); 20055682Smarkm op = tmp; 20155682Smarkm krb5_ret_int32 (sp, &len); 202233294Sstas if ((uint32_t)vers <= server_context->log_context.version) 203178825Sdfr krb5_storage_seek(sp, len + 8, SEEK_CUR); 204233294Sstas } while((uint32_t)vers <= server_context->log_context.version); 20555682Smarkm 206178825Sdfr /* 207178825Sdfr * Read up rest of the entires into the memory... 208178825Sdfr */ 209102644Snectar left = krb5_storage_seek (sp, -16, SEEK_CUR); 210102644Snectar right = krb5_storage_seek (sp, 0, SEEK_END); 21155682Smarkm buf = malloc (right - left); 212178825Sdfr if (buf == NULL && (right - left) != 0) 213178825Sdfr krb5_errx (context, 1, "malloc: no memory"); 214178825Sdfr 215178825Sdfr /* 216178825Sdfr * ...and then write them out to the on-disk log. 217178825Sdfr */ 218102644Snectar krb5_storage_seek (sp, left, SEEK_SET); 219102644Snectar krb5_storage_read (sp, buf, right - left); 220178825Sdfr sret = write (server_context->log_context.log_fd, buf, right-left); 221178825Sdfr if (sret != right - left) 222178825Sdfr krb5_err(context, 1, errno, "Failed to write log to disk"); 223178825Sdfr ret = fsync (server_context->log_context.log_fd); 224178825Sdfr if (ret) 225178825Sdfr krb5_err(context, 1, errno, "Failed to sync log to disk"); 22655682Smarkm free (buf); 22755682Smarkm 228178825Sdfr /* 229178825Sdfr * Go back to the startpoint and start to commit the entires to 230178825Sdfr * the database. 231178825Sdfr */ 232102644Snectar krb5_storage_seek (sp, left, SEEK_SET); 23355682Smarkm 23455682Smarkm for(;;) { 235178825Sdfr int32_t len, len2, timestamp, tmp; 236178825Sdfr off_t cur, cur2; 23755682Smarkm enum kadm_ops op; 23855682Smarkm 23955682Smarkm if(krb5_ret_int32 (sp, &vers) != 0) 24055682Smarkm break; 241178825Sdfr ret = krb5_ret_int32 (sp, ×tamp); 242178825Sdfr if (ret) krb5_errx(context, 1, "entry %ld: too short", (long)vers); 243178825Sdfr ret = krb5_ret_int32 (sp, &tmp); 244178825Sdfr if (ret) krb5_errx(context, 1, "entry %ld: too short", (long)vers); 24555682Smarkm op = tmp; 246178825Sdfr ret = krb5_ret_int32 (sp, &len); 247178825Sdfr if (ret) krb5_errx(context, 1, "entry %ld: too short", (long)vers); 248178825Sdfr if (len < 0) 249178825Sdfr krb5_errx(context, 1, "log is corrupted, " 250233294Sstas "negative length of entry version %ld: %ld", 251233294Sstas (long)vers, (long)len); 252178825Sdfr cur = krb5_storage_seek(sp, 0, SEEK_CUR); 25355682Smarkm 254178825Sdfr krb5_warnx (context, "replaying entry %d", (int)vers); 255178825Sdfr 25655682Smarkm ret = kadm5_log_replay (server_context, 25755682Smarkm op, vers, len, sp); 258178825Sdfr if (ret) { 259233294Sstas const char *s = krb5_get_error_message(server_context->context, ret); 260178825Sdfr krb5_warnx (context, 261233294Sstas "kadm5_log_replay: %ld. Lost entry entry, " 262233294Sstas "Database out of sync ?: %s (%d)", 263178825Sdfr (long)vers, s ? s : "unknown error", ret); 264233294Sstas krb5_free_error_message(context, s); 265178825Sdfr } 266178825Sdfr 267178825Sdfr { 268233294Sstas /* 269178825Sdfr * Make sure the krb5_log_replay does the right thing wrt 270178825Sdfr * reading out data from the sp. 271178825Sdfr */ 272178825Sdfr cur2 = krb5_storage_seek(sp, 0, SEEK_CUR); 273178825Sdfr if (cur + len != cur2) 274233294Sstas krb5_errx(context, 1, 275178825Sdfr "kadm5_log_reply version: %ld didn't read the whole entry", 276178825Sdfr (long)vers); 277178825Sdfr } 278178825Sdfr 279178825Sdfr if (krb5_ret_int32 (sp, &len2) != 0) 280178825Sdfr krb5_errx(context, 1, "entry %ld: postamble too short", (long)vers); 281178825Sdfr if(krb5_ret_int32 (sp, &vers2) != 0) 282178825Sdfr krb5_errx(context, 1, "entry %ld: postamble too short", (long)vers); 283178825Sdfr 284178825Sdfr if (len != len2) 285178825Sdfr krb5_errx(context, 1, "entry %ld: len != len2", (long)vers); 286178825Sdfr if (vers != vers2) 287178825Sdfr krb5_errx(context, 1, "entry %ld: vers != vers2", (long)vers); 28855682Smarkm } 289178825Sdfr 290178825Sdfr /* 291178825Sdfr * Update version 292178825Sdfr */ 293178825Sdfr 294178825Sdfr server_context->log_context.version = vers; 29572445Sassar} 29655682Smarkm 29772445Sassarstatic void 29872445Sassarreceive (krb5_context context, 29972445Sassar krb5_storage *sp, 30072445Sassar kadm5_server_context *server_context) 30172445Sassar{ 30272445Sassar int ret; 30372445Sassar 304178825Sdfr ret = server_context->db->hdb_open(context, 305178825Sdfr server_context->db, 306178825Sdfr O_RDWR | O_CREAT, 0600); 30772445Sassar if (ret) 30872445Sassar krb5_err (context, 1, ret, "db->open"); 30972445Sassar 31072445Sassar receive_loop (context, sp, server_context); 31172445Sassar 312178825Sdfr ret = server_context->db->hdb_close (context, server_context->db); 31355682Smarkm if (ret) 31455682Smarkm krb5_err (context, 1, ret, "db->close"); 31555682Smarkm} 31655682Smarkm 31772445Sassarstatic void 318178825Sdfrsend_im_here (krb5_context context, int fd, 319178825Sdfr krb5_auth_context auth_context) 320178825Sdfr{ 321178825Sdfr krb5_storage *sp; 322178825Sdfr krb5_data data; 323178825Sdfr int ret; 324178825Sdfr 325178825Sdfr ret = krb5_data_alloc (&data, 4); 326178825Sdfr if (ret) 327178825Sdfr krb5_err (context, 1, ret, "send_im_here"); 328178825Sdfr 329178825Sdfr sp = krb5_storage_from_data (&data); 330178825Sdfr if (sp == NULL) 331178825Sdfr krb5_errx (context, 1, "krb5_storage_from_data"); 332178825Sdfr krb5_store_int32(sp, I_AM_HERE); 333178825Sdfr krb5_storage_free(sp); 334178825Sdfr 335178825Sdfr ret = krb5_write_priv_message(context, auth_context, &fd, &data); 336178825Sdfr krb5_data_free(&data); 337178825Sdfr 338178825Sdfr if (ret) 339178825Sdfr krb5_err (context, 1, ret, "krb5_write_priv_message"); 340178825Sdfr} 341178825Sdfr 342233294Sstasstatic krb5_error_code 34372445Sassarreceive_everything (krb5_context context, int fd, 34472445Sassar kadm5_server_context *server_context, 34572445Sassar krb5_auth_context auth_context) 34672445Sassar{ 34772445Sassar int ret; 34872445Sassar krb5_data data; 349233294Sstas int32_t vno = 0; 35072445Sassar int32_t opcode; 351178825Sdfr krb5_storage *sp; 35272445Sassar 353107207Snectar char *dbname; 354107207Snectar HDB *mydb; 355233294Sstas 356178825Sdfr krb5_warnx(context, "receive complete database"); 357178825Sdfr 358178825Sdfr asprintf(&dbname, "%s-NEW", server_context->db->hdb_name); 359107207Snectar ret = hdb_create(context, &mydb, dbname); 360107207Snectar if(ret) 361107207Snectar krb5_err(context,1, ret, "hdb_create"); 362107207Snectar free(dbname); 363233294Sstas 364107207Snectar ret = hdb_set_master_keyfile (context, 365107207Snectar mydb, server_context->config.stash_file); 366107207Snectar if(ret) 367107207Snectar krb5_err(context,1, ret, "hdb_set_master_keyfile"); 368233294Sstas 369107207Snectar /* I really want to use O_EXCL here, but given that I can't easily clean 370107207Snectar up on error, I won't */ 371178825Sdfr ret = mydb->hdb_open(context, mydb, O_RDWR | O_CREAT | O_TRUNC, 0600); 37272445Sassar if (ret) 37372445Sassar krb5_err (context, 1, ret, "db->open"); 37472445Sassar 375178825Sdfr sp = NULL; 37672445Sassar do { 37772445Sassar ret = krb5_read_priv_message(context, auth_context, &fd, &data); 37872445Sassar 379233294Sstas if (ret) { 380233294Sstas krb5_warn (context, ret, "krb5_read_priv_message"); 381233294Sstas goto cleanup; 382233294Sstas } 38372445Sassar 38472445Sassar sp = krb5_storage_from_data (&data); 385178825Sdfr if (sp == NULL) 386178825Sdfr krb5_errx (context, 1, "krb5_storage_from_data"); 38772445Sassar krb5_ret_int32 (sp, &opcode); 38872445Sassar if (opcode == ONE_PRINC) { 38972445Sassar krb5_data fake_data; 390178825Sdfr hdb_entry_ex entry; 39172445Sassar 392178825Sdfr krb5_storage_free(sp); 393178825Sdfr 39472445Sassar fake_data.data = (char *)data.data + 4; 39572445Sassar fake_data.length = data.length - 4; 39672445Sassar 397178825Sdfr memset(&entry, 0, sizeof(entry)); 398178825Sdfr 399178825Sdfr ret = hdb_value2entry (context, &fake_data, &entry.entry); 40072445Sassar if (ret) 40172445Sassar krb5_err (context, 1, ret, "hdb_value2entry"); 402178825Sdfr ret = mydb->hdb_store(server_context->context, 403178825Sdfr mydb, 404178825Sdfr 0, &entry); 40572445Sassar if (ret) 40672445Sassar krb5_err (context, 1, ret, "hdb_store"); 40772445Sassar 40872445Sassar hdb_free_entry (context, &entry); 40972445Sassar krb5_data_free (&data); 410178825Sdfr } else if (opcode == NOW_YOU_HAVE) 411178825Sdfr ; 412178825Sdfr else 413178825Sdfr krb5_errx (context, 1, "strange opcode %d", opcode); 41472445Sassar } while (opcode == ONE_PRINC); 41572445Sassar 41672445Sassar if (opcode != NOW_YOU_HAVE) 41772445Sassar krb5_errx (context, 1, "receive_everything: strange %d", opcode); 41872445Sassar 419178825Sdfr krb5_ret_int32 (sp, &vno); 420178825Sdfr krb5_storage_free(sp); 42172445Sassar 42272445Sassar ret = kadm5_log_reinit (server_context); 42372445Sassar if (ret) 42472445Sassar krb5_err(context, 1, ret, "kadm5_log_reinit"); 42572445Sassar 42672445Sassar ret = kadm5_log_set_version (server_context, vno - 1); 42772445Sassar if (ret) 42872445Sassar krb5_err (context, 1, ret, "kadm5_log_set_version"); 42972445Sassar 43072445Sassar ret = kadm5_log_nop (server_context); 43172445Sassar if (ret) 43272445Sassar krb5_err (context, 1, ret, "kadm5_log_nop"); 43372445Sassar 434178825Sdfr ret = mydb->hdb_rename (context, mydb, server_context->db->hdb_name); 435127808Snectar if (ret) 436127808Snectar krb5_err (context, 1, ret, "db->rename"); 437127808Snectar 438233294Sstas cleanup: 439233294Sstas krb5_data_free (&data); 440233294Sstas 441178825Sdfr ret = mydb->hdb_close (context, mydb); 44272445Sassar if (ret) 44372445Sassar krb5_err (context, 1, ret, "db->close"); 444127808Snectar 445178825Sdfr ret = mydb->hdb_destroy (context, mydb); 446107207Snectar if (ret) 447107207Snectar krb5_err (context, 1, ret, "db->destroy"); 448178825Sdfr 449178825Sdfr krb5_warnx(context, "receive complete database, version %ld", (long)vno); 450233294Sstas return ret; 45172445Sassar} 45272445Sassar 453178825Sdfrstatic char *config_file; 45472445Sassarstatic char *realm; 45572445Sassarstatic int version_flag; 45672445Sassarstatic int help_flag; 45772445Sassarstatic char *keytab_str; 458178825Sdfrstatic char *port_str; 459233294Sstas#ifdef SUPPORT_DETACH 460178825Sdfrstatic int detach_from_console = 0; 461233294Sstas#endif 46272445Sassar 46372445Sassarstatic struct getargs args[] = { 464233294Sstas { "config-file", 'c', arg_string, &config_file, NULL, NULL }, 465233294Sstas { "realm", 'r', arg_string, &realm, NULL, NULL }, 46672445Sassar { "keytab", 'k', arg_string, &keytab_str, 46772445Sassar "keytab to get authentication from", "kspec" }, 468178825Sdfr { "time-lost", 0, arg_string, &server_time_lost, 469178825Sdfr "time before server is considered lost", "time" }, 470178825Sdfr { "port", 0, arg_string, &port_str, 471178825Sdfr "port ipropd-slave will connect to", "port"}, 472233294Sstas#ifdef SUPPORT_DETACH 473233294Sstas { "detach", 0, arg_flag, &detach_from_console, 474233294Sstas "detach from console", NULL }, 475233294Sstas#endif 476233294Sstas { "hostname", 0, arg_string, rk_UNCONST(&slave_str), 477178825Sdfr "hostname of slave (if not same as hostname)", "hostname" }, 478233294Sstas { "version", 0, arg_flag, &version_flag, NULL, NULL }, 479233294Sstas { "help", 0, arg_flag, &help_flag, NULL, NULL } 48055682Smarkm}; 48155682Smarkm 48272445Sassarstatic int num_args = sizeof(args) / sizeof(args[0]); 48372445Sassar 484233294Sstasstatic void 485233294Sstasusage(int status) 486233294Sstas{ 487233294Sstas arg_printusage(args, num_args, NULL, "master"); 488233294Sstas exit(status); 489233294Sstas} 490233294Sstas 49155682Smarkmint 49255682Smarkmmain(int argc, char **argv) 49355682Smarkm{ 49455682Smarkm krb5_error_code ret; 49555682Smarkm krb5_context context; 49655682Smarkm krb5_auth_context auth_context; 49755682Smarkm void *kadm_handle; 49855682Smarkm kadm5_server_context *server_context; 49955682Smarkm kadm5_config_params conf; 50055682Smarkm int master_fd; 50155682Smarkm krb5_ccache ccache; 50255682Smarkm krb5_principal server; 503178825Sdfr char **files; 504233294Sstas int optidx = 0; 505233294Sstas time_t reconnect_min; 506233294Sstas time_t backoff; 507233294Sstas time_t reconnect_max; 508233294Sstas time_t reconnect; 509233294Sstas time_t before = 0; 51055682Smarkm 51172445Sassar const char *master; 512233294Sstas 513233294Sstas setprogname(argv[0]); 514233294Sstas 515233294Sstas if(getarg(args, num_args, argc, argv, &optidx)) 516233294Sstas usage(1); 517233294Sstas 51855682Smarkm if(help_flag) 519233294Sstas usage(0); 52055682Smarkm if(version_flag) { 52155682Smarkm print_version(NULL); 52255682Smarkm exit(0); 52355682Smarkm } 52455682Smarkm 525233294Sstas ret = krb5_init_context(&context); 526233294Sstas if (ret) 527233294Sstas errx (1, "krb5_init_context failed: %d", ret); 528233294Sstas 529178825Sdfr setup_signal(); 53072445Sassar 531178825Sdfr if (config_file == NULL) { 532233294Sstas if (asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context)) == -1 533233294Sstas || config_file == NULL) 534178825Sdfr errx(1, "out of memory"); 535178825Sdfr } 536178825Sdfr 537178825Sdfr ret = krb5_prepend_config_files_default(config_file, &files); 538178825Sdfr if (ret) 539178825Sdfr krb5_err(context, 1, ret, "getting configuration files"); 540178825Sdfr 541178825Sdfr ret = krb5_set_config_files(context, files); 542178825Sdfr krb5_free_config_files(files); 543178825Sdfr if (ret) 544178825Sdfr krb5_err(context, 1, ret, "reading configuration files"); 545178825Sdfr 546178825Sdfr argc -= optidx; 547178825Sdfr argv += optidx; 548178825Sdfr 54972445Sassar if (argc != 1) 550233294Sstas usage(1); 55172445Sassar 55272445Sassar master = argv[0]; 55372445Sassar 554233294Sstas#ifdef SUPPORT_DETACH 555178825Sdfr if (detach_from_console) 556178825Sdfr daemon(0, 0); 557233294Sstas#endif 55890926Snectar pidfile (NULL); 55990926Snectar krb5_openlog (context, "ipropd-slave", &log_facility); 56072445Sassar krb5_set_warn_dest(context, log_facility); 56172445Sassar 56272445Sassar ret = krb5_kt_register(context, &hdb_kt_ops); 56372445Sassar if(ret) 56472445Sassar krb5_err(context, 1, ret, "krb5_kt_register"); 56572445Sassar 566178825Sdfr time_before_lost = parse_time (server_time_lost, "s"); 567178825Sdfr if (time_before_lost < 0) 568178825Sdfr krb5_errx (context, 1, "couldn't parse time: %s", server_time_lost); 569178825Sdfr 57055682Smarkm memset(&conf, 0, sizeof(conf)); 57155682Smarkm if(realm) { 57255682Smarkm conf.mask |= KADM5_CONFIG_REALM; 57355682Smarkm conf.realm = realm; 57455682Smarkm } 57555682Smarkm ret = kadm5_init_with_password_ctx (context, 57655682Smarkm KADM5_ADMIN_SERVICE, 57755682Smarkm NULL, 57855682Smarkm KADM5_ADMIN_SERVICE, 579233294Sstas &conf, 0, 0, 58055682Smarkm &kadm_handle); 58155682Smarkm if (ret) 58255682Smarkm krb5_err (context, 1, ret, "kadm5_init_with_password_ctx"); 58355682Smarkm 58455682Smarkm server_context = (kadm5_server_context *)kadm_handle; 58555682Smarkm 58655682Smarkm ret = kadm5_log_init (server_context); 58755682Smarkm if (ret) 58855682Smarkm krb5_err (context, 1, ret, "kadm5_log_init"); 58955682Smarkm 59072445Sassar get_creds(context, keytab_str, &ccache, master); 59155682Smarkm 59272445Sassar ret = krb5_sname_to_principal (context, master, IPROP_NAME, 59355682Smarkm KRB5_NT_SRV_HST, &server); 59455682Smarkm if (ret) 59555682Smarkm krb5_err (context, 1, ret, "krb5_sname_to_principal"); 59655682Smarkm 59755682Smarkm auth_context = NULL; 598233294Sstas master_fd = -1; 59955682Smarkm 600233294Sstas krb5_appdefault_time(context, config_name, NULL, "reconnect-min", 601233294Sstas 10, &reconnect_min); 602233294Sstas krb5_appdefault_time(context, config_name, NULL, "reconnect-max", 603233294Sstas 300, &reconnect_max); 604233294Sstas krb5_appdefault_time(context, config_name, NULL, "reconnect-backoff", 605233294Sstas 10, &backoff); 606233294Sstas reconnect = reconnect_min; 607178825Sdfr 608233294Sstas while (!exit_flag) { 609233294Sstas time_t now, elapsed; 610233294Sstas int connected = FALSE; 61155682Smarkm 612233294Sstas now = time(NULL); 613233294Sstas elapsed = now - before; 61455682Smarkm 615233294Sstas if (elapsed < reconnect) { 616233294Sstas time_t left = reconnect - elapsed; 617233294Sstas krb5_warnx(context, "sleeping %d seconds before " 618233294Sstas "retrying to connect", (int)left); 619233294Sstas sleep(left); 620233294Sstas } 621233294Sstas before = now; 622178825Sdfr 623233294Sstas master_fd = connect_to_master (context, master, port_str); 624233294Sstas if (master_fd < 0) 625233294Sstas goto retry; 626178825Sdfr 627233294Sstas reconnect = reconnect_min; 628178825Sdfr 629233294Sstas if (auth_context) { 630233294Sstas krb5_auth_con_free(context, auth_context); 631233294Sstas auth_context = NULL; 632233294Sstas krb5_cc_destroy(context, ccache); 633233294Sstas get_creds(context, keytab_str, &ccache, master); 634178825Sdfr } 635233294Sstas ret = krb5_sendauth (context, &auth_context, &master_fd, 636233294Sstas IPROP_VERSION, NULL, server, 637233294Sstas AP_OPTS_MUTUAL_REQUIRED, NULL, NULL, 638233294Sstas ccache, NULL, NULL, NULL); 639233294Sstas if (ret) { 640233294Sstas krb5_warn (context, ret, "krb5_sendauth"); 641233294Sstas goto retry; 642233294Sstas } 643178825Sdfr 644233294Sstas krb5_warnx(context, "ipropd-slave started at version: %ld", 645233294Sstas (long)server_context->log_context.version); 64655682Smarkm 647233294Sstas ret = ihave (context, auth_context, master_fd, 648233294Sstas server_context->log_context.version); 64955682Smarkm if (ret) 650233294Sstas goto retry; 65155682Smarkm 652233294Sstas connected = TRUE; 653233294Sstas 654233294Sstas while (connected && !exit_flag) { 655233294Sstas krb5_data out; 656233294Sstas krb5_storage *sp; 657233294Sstas int32_t tmp; 658233294Sstas fd_set readset; 659233294Sstas struct timeval to; 660233294Sstas 661233294Sstas#ifndef NO_LIMIT_FD_SETSIZE 662233294Sstas if (master_fd >= FD_SETSIZE) 663233294Sstas krb5_errx (context, 1, "fd too large"); 664233294Sstas#endif 665233294Sstas 666233294Sstas FD_ZERO(&readset); 667233294Sstas FD_SET(master_fd, &readset); 668233294Sstas 669233294Sstas to.tv_sec = time_before_lost; 670233294Sstas to.tv_usec = 0; 671233294Sstas 672233294Sstas ret = select (master_fd + 1, 673233294Sstas &readset, NULL, NULL, &to); 674233294Sstas if (ret < 0) { 675233294Sstas if (errno == EINTR) 676233294Sstas continue; 677233294Sstas else 678233294Sstas krb5_err (context, 1, errno, "select"); 679233294Sstas } 680233294Sstas if (ret == 0) 681233294Sstas krb5_errx (context, 1, "server didn't send a message " 682233294Sstas "in %d seconds", time_before_lost); 683233294Sstas 684233294Sstas ret = krb5_read_priv_message(context, auth_context, &master_fd, &out); 685233294Sstas if (ret) { 686233294Sstas krb5_warn (context, ret, "krb5_read_priv_message"); 687233294Sstas connected = FALSE; 688233294Sstas continue; 689233294Sstas } 690233294Sstas 691233294Sstas sp = krb5_storage_from_mem (out.data, out.length); 692233294Sstas krb5_ret_int32 (sp, &tmp); 693233294Sstas switch (tmp) { 694233294Sstas case FOR_YOU : 695233294Sstas receive (context, sp, server_context); 696233294Sstas ret = ihave (context, auth_context, master_fd, 697233294Sstas server_context->log_context.version); 698233294Sstas if (ret) 699233294Sstas connected = FALSE; 700233294Sstas break; 701233294Sstas case TELL_YOU_EVERYTHING : 702233294Sstas ret = receive_everything (context, master_fd, server_context, 703233294Sstas auth_context); 704233294Sstas if (ret) 705233294Sstas connected = FALSE; 706233294Sstas break; 707233294Sstas case ARE_YOU_THERE : 708233294Sstas send_im_here (context, master_fd, auth_context); 709233294Sstas break; 710233294Sstas case NOW_YOU_HAVE : 711233294Sstas case I_HAVE : 712233294Sstas case ONE_PRINC : 713233294Sstas case I_AM_HERE : 714233294Sstas default : 715233294Sstas krb5_warnx (context, "Ignoring command %d", tmp); 716233294Sstas break; 717233294Sstas } 718233294Sstas krb5_storage_free (sp); 719233294Sstas krb5_data_free (&out); 720233294Sstas 72155682Smarkm } 722233294Sstas retry: 723233294Sstas if (connected == FALSE) 724233294Sstas krb5_warnx (context, "disconnected for server"); 725233294Sstas if (exit_flag) 726233294Sstas krb5_warnx (context, "got an exit signal"); 727233294Sstas 728233294Sstas if (master_fd >= 0) 729233294Sstas close(master_fd); 730233294Sstas 731233294Sstas reconnect += backoff; 732233294Sstas if (reconnect > reconnect_max) 733233294Sstas reconnect = reconnect_max; 73455682Smarkm } 735233294Sstas 736233294Sstas if (0); 737233294Sstas#ifndef NO_SIGXCPU 738233294Sstas else if(exit_flag == SIGXCPU) 739178825Sdfr krb5_warnx(context, "%s CPU time limit exceeded", getprogname()); 740233294Sstas#endif 741178825Sdfr else if(exit_flag == SIGINT || exit_flag == SIGTERM) 742178825Sdfr krb5_warnx(context, "%s terminated", getprogname()); 743178825Sdfr else 744233294Sstas krb5_warnx(context, "%s unexpected exit reason: %ld", 745233294Sstas getprogname(), (long)exit_flag); 746178825Sdfr 74755682Smarkm return 0; 74890926Snectar} 749