1/*	$NetBSD$	*/
2
3/*
4 * Copyright (c) 1997-2006 Kungliga Tekniska H��gskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "hprop.h"
37
38static int inetd_flag = -1;
39static int help_flag;
40static int version_flag;
41static int print_dump;
42static const char *database;
43static int from_stdin;
44static char *local_realm;
45static char *ktname = NULL;
46
47struct getargs args[] = {
48    { "database", 'd', arg_string, rk_UNCONST(&database), "database", "file" },
49    { "stdin",    'n', arg_flag, &from_stdin, "read from stdin" },
50    { "print",	    0, arg_flag, &print_dump, "print dump to stdout" },
51#ifdef SUPPORT_INETD
52    { "inetd",	   'i',	arg_negative_flag,	&inetd_flag,
53      "Not started from inetd" },
54#endif
55    { "keytab",   'k',	arg_string, &ktname,	"keytab to use for authentication", "keytab" },
56    { "realm",   'r',	arg_string, &local_realm, "realm to use" },
57    { "version",    0, arg_flag, &version_flag, NULL, NULL },
58    { "help",    'h',  arg_flag, &help_flag, NULL, NULL}
59};
60
61static int num_args = sizeof(args) / sizeof(args[0]);
62
63static void
64usage(int ret)
65{
66    arg_printusage (args, num_args, NULL, "");
67    exit (ret);
68}
69
70int
71main(int argc, char **argv)
72{
73    krb5_error_code ret;
74    krb5_context context;
75    krb5_auth_context ac = NULL;
76    krb5_principal c1, c2;
77    krb5_authenticator authent;
78    krb5_keytab keytab;
79    krb5_socket_t sock = rk_INVALID_SOCKET;
80    HDB *db = NULL;
81    int optidx = 0;
82    char *tmp_db;
83    krb5_log_facility *fac;
84    int nprincs;
85
86    setprogname(argv[0]);
87
88    ret = krb5_init_context(&context);
89    if(ret)
90	exit(1);
91
92    ret = krb5_openlog(context, "hpropd", &fac);
93    if(ret)
94	errx(1, "krb5_openlog");
95    krb5_set_warn_dest(context, fac);
96
97    if(getarg(args, num_args, argc, argv, &optidx))
98	usage(1);
99
100    if(local_realm != NULL)
101	krb5_set_default_realm(context, local_realm);
102
103    if(help_flag)
104	usage(0);
105    if(version_flag) {
106	print_version(NULL);
107	exit(0);
108    }
109
110    argc -= optidx;
111    argv += optidx;
112
113    if (argc != 0)
114	usage(1);
115
116    if (database == NULL)
117	database = hdb_default_db(context);
118
119    if(from_stdin) {
120	sock = STDIN_FILENO;
121    } else {
122	struct sockaddr_storage ss;
123	struct sockaddr *sa = (struct sockaddr *)&ss;
124	socklen_t sin_len = sizeof(ss);
125	char addr_name[256];
126	krb5_ticket *ticket;
127	char *server;
128
129	sock = STDIN_FILENO;
130#ifdef SUPPORT_INETD
131	if (inetd_flag == -1) {
132	    if (getpeername (sock, sa, &sin_len) < 0) {
133		inetd_flag = 0;
134	    } else {
135		inetd_flag = 1;
136	    }
137	}
138#else
139	inetd_flag = 0;
140#endif
141	if (!inetd_flag) {
142	    mini_inetd (krb5_getportbyname (context, "hprop", "tcp",
143					    HPROP_PORT), &sock);
144	}
145	sin_len = sizeof(ss);
146	if(getpeername(sock, sa, &sin_len) < 0)
147	    krb5_err(context, 1, errno, "getpeername");
148
149	if (inet_ntop(sa->sa_family,
150		      socket_get_address (sa),
151		      addr_name,
152		      sizeof(addr_name)) == NULL)
153	    strlcpy (addr_name, "unknown address",
154		     sizeof(addr_name));
155
156	krb5_log(context, fac, 0, "Connection from %s", addr_name);
157
158	ret = krb5_kt_register(context, &hdb_kt_ops);
159	if(ret)
160	    krb5_err(context, 1, ret, "krb5_kt_register");
161
162	if (ktname != NULL) {
163	    ret = krb5_kt_resolve(context, ktname, &keytab);
164	    if (ret)
165		krb5_err (context, 1, ret, "krb5_kt_resolve %s", ktname);
166	} else {
167	    ret = krb5_kt_default (context, &keytab);
168	    if (ret)
169		krb5_err (context, 1, ret, "krb5_kt_default");
170	}
171
172	ret = krb5_recvauth(context, &ac, &sock, HPROP_VERSION, NULL,
173			    0, keytab, &ticket);
174	if(ret)
175	    krb5_err(context, 1, ret, "krb5_recvauth");
176
177	ret = krb5_unparse_name(context, ticket->server, &server);
178	if (ret)
179	    krb5_err(context, 1, ret, "krb5_unparse_name");
180	if (strncmp(server, "hprop/", 5) != 0)
181	    krb5_errx(context, 1, "ticket not for hprop (%s)", server);
182
183	free(server);
184	krb5_free_ticket (context, ticket);
185
186	ret = krb5_auth_con_getauthenticator(context, ac, &authent);
187	if(ret)
188	    krb5_err(context, 1, ret, "krb5_auth_con_getauthenticator");
189
190	ret = krb5_make_principal(context, &c1, NULL, "kadmin", "hprop", NULL);
191	if(ret)
192	    krb5_err(context, 1, ret, "krb5_make_principal");
193	_krb5_principalname2krb5_principal(context, &c2,
194					   authent->cname, authent->crealm);
195	if(!krb5_principal_compare(context, c1, c2)) {
196	    char *s;
197	    ret = krb5_unparse_name(context, c2, &s);
198	    if (ret)
199		s = "unparseable name";
200	    krb5_errx(context, 1, "Unauthorized connection from %s", s);
201	}
202	krb5_free_principal(context, c1);
203	krb5_free_principal(context, c2);
204
205	ret = krb5_kt_close(context, keytab);
206	if(ret)
207	    krb5_err(context, 1, ret, "krb5_kt_close");
208    }
209
210    if(!print_dump) {
211	asprintf(&tmp_db, "%s~", database);
212
213	ret = hdb_create(context, &db, tmp_db);
214	if(ret)
215	    krb5_err(context, 1, ret, "hdb_create(%s)", tmp_db);
216	ret = db->hdb_open(context, db, O_RDWR | O_CREAT | O_TRUNC, 0600);
217	if(ret)
218	    krb5_err(context, 1, ret, "hdb_open(%s)", tmp_db);
219    }
220
221    nprincs = 0;
222    while(1){
223	krb5_data data;
224	hdb_entry_ex entry;
225
226	if(from_stdin) {
227	    ret = krb5_read_message(context, &sock, &data);
228	    if(ret != 0 && ret != HEIM_ERR_EOF)
229		krb5_err(context, 1, ret, "krb5_read_message");
230	} else {
231	    ret = krb5_read_priv_message(context, ac, &sock, &data);
232	    if(ret)
233		krb5_err(context, 1, ret, "krb5_read_priv_message");
234	}
235
236	if(ret == HEIM_ERR_EOF || data.length == 0) {
237	    if(!from_stdin) {
238		data.data = NULL;
239		data.length = 0;
240		krb5_write_priv_message(context, ac, &sock, &data);
241	    }
242	    if(!print_dump) {
243		ret = db->hdb_close(context, db);
244		if(ret)
245		    krb5_err(context, 1, ret, "db_close");
246		ret = db->hdb_rename(context, db, database);
247		if(ret)
248		    krb5_err(context, 1, ret, "db_rename");
249	    }
250	    break;
251	}
252	memset(&entry, 0, sizeof(entry));
253	ret = hdb_value2entry(context, &data, &entry.entry);
254	krb5_data_free(&data);
255	if(ret)
256	    krb5_err(context, 1, ret, "hdb_value2entry");
257	if(print_dump)
258	    hdb_print_entry(context, db, &entry, stdout);
259	else {
260	    ret = db->hdb_store(context, db, 0, &entry);
261	    if(ret == HDB_ERR_EXISTS) {
262		char *s;
263		ret = krb5_unparse_name(context, entry.entry.principal, &s);
264		if (ret)
265		    s = strdup("unparseable name");
266		krb5_warnx(context, "Entry exists: %s", s);
267		free(s);
268	    } else if(ret)
269		krb5_err(context, 1, ret, "db_store");
270	    else
271		nprincs++;
272	}
273	hdb_free_entry(context, &entry);
274    }
275    if (!print_dump)
276	krb5_log(context, fac, 0, "Received %d principals", nprincs);
277
278    if (inetd_flag == 0)
279	rk_closesocket(sock);
280
281    exit(0);
282}
283