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, &timestamp);
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, &timestamp);
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