xref: /freebsd/crypto/heimdal/lib/kadm5/ipropd_master.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
1 /*
2  * Copyright (c) 1997 - 2001 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include "iprop.h"
35 
36 RCSID("$Id: ipropd_master.c,v 1.22 2001/02/14 23:00:16 assar Exp $");
37 
38 static krb5_log_facility *log_facility;
39 
40 static int
41 make_signal_socket (krb5_context context)
42 {
43     struct sockaddr_un addr;
44     int fd;
45 
46     fd = socket (AF_UNIX, SOCK_DGRAM, 0);
47     if (fd < 0)
48 	krb5_err (context, 1, errno, "socket AF_UNIX");
49     memset (&addr, 0, sizeof(addr));
50     addr.sun_family = AF_UNIX;
51     strlcpy (addr.sun_path, KADM5_LOG_SIGNAL, sizeof(addr.sun_path));
52     unlink (addr.sun_path);
53     if (bind (fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
54 	krb5_err (context, 1, errno, "bind %s", addr.sun_path);
55     return fd;
56 }
57 
58 static int
59 make_listen_socket (krb5_context context)
60 {
61     int fd;
62     int one = 1;
63     struct sockaddr_in addr;
64 
65     fd = socket (AF_INET, SOCK_STREAM, 0);
66     if (fd < 0)
67 	krb5_err (context, 1, errno, "socket AF_INET");
68     setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
69     memset (&addr, 0, sizeof(addr));
70     addr.sin_family = AF_INET;
71     addr.sin_port   = krb5_getportbyname (context,
72 					  IPROP_SERVICE, "tcp", IPROP_PORT);
73     if(bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
74 	krb5_err (context, 1, errno, "bind");
75     if (listen(fd, SOMAXCONN) < 0)
76 	krb5_err (context, 1, errno, "listen");
77     return fd;
78 }
79 
80 struct slave {
81     int fd;
82     struct sockaddr_in addr;
83     char *name;
84     krb5_auth_context ac;
85     u_int32_t version;
86     struct slave *next;
87 };
88 
89 typedef struct slave slave;
90 
91 static int
92 check_acl (krb5_context context, const char *name)
93 {
94     FILE *fp;
95     char buf[256];
96     int ret = 1;
97 
98     fp = fopen (KADM5_SLAVE_ACL, "r");
99     if (fp == NULL)
100 	return 1;
101     while (fgets(buf, sizeof(buf), fp) != NULL) {
102 	if (buf[strlen(buf) - 1 ] == '\n')
103 	    buf[strlen(buf) - 1 ] = '\0';
104 	if (strcmp (buf, name) == 0) {
105 	    ret = 0;
106 	    break;
107 	}
108     }
109     fclose (fp);
110     return ret;
111 }
112 
113 static void
114 add_slave (krb5_context context, krb5_keytab keytab, slave **root, int fd)
115 {
116     krb5_principal server;
117     krb5_error_code ret;
118     slave *s;
119     socklen_t addr_len;
120     krb5_ticket *ticket = NULL;
121     char hostname[128];
122 
123     s = malloc(sizeof(*s));
124     if (s == NULL) {
125 	krb5_warnx (context, "add_slave: no memory");
126 	return;
127     }
128     s->name = NULL;
129     s->ac = NULL;
130 
131     addr_len = sizeof(s->addr);
132     s->fd = accept (fd, (struct sockaddr *)&s->addr, &addr_len);
133     if (s->fd < 0) {
134 	krb5_warn (context, errno, "accept");
135 	goto error;
136     }
137     gethostname(hostname, sizeof(hostname));
138     ret = krb5_sname_to_principal (context, hostname, IPROP_NAME,
139 				   KRB5_NT_SRV_HST, &server);
140     if (ret) {
141 	krb5_warn (context, ret, "krb5_sname_to_principal");
142 	goto error;
143     }
144 
145     ret = krb5_recvauth (context, &s->ac, &s->fd,
146 			 IPROP_VERSION, server, 0, keytab, &ticket);
147     krb5_free_principal (context, server);
148     if (ret) {
149 	krb5_warn (context, ret, "krb5_recvauth");
150 	goto error;
151     }
152     ret = krb5_unparse_name (context, ticket->client, &s->name);
153     if (ret) {
154 	krb5_warn (context, ret, "krb5_unparse_name");
155 	goto error;
156     }
157     if (check_acl (context, s->name)) {
158 	krb5_warnx (context, "%s not in acl", s->name);
159 	goto error;
160     }
161     krb5_free_ticket (context, ticket);
162     krb5_warnx (context, "connection from %s", s->name);
163 
164     s->version = 0;
165     s->next = *root;
166     *root = s;
167     return;
168 error:
169     if (s->name)
170 	free (s->name);
171     if (s->ac)
172 	krb5_auth_con_free(context, s->ac);
173     if (ticket)
174     krb5_free_ticket (context, ticket);
175     close (s->fd);
176     free(s);
177 }
178 
179 static void
180 remove_slave (krb5_context context, slave *s, slave **root)
181 {
182     slave **p;
183 
184     close (s->fd);
185     free (s->name);
186     krb5_auth_con_free (context, s->ac);
187 
188     for (p = root; *p; p = &(*p)->next)
189 	if (*p == s) {
190 	    *p = s->next;
191 	    break;
192 	}
193     free (s);
194 }
195 
196 struct prop_context {
197     krb5_auth_context auth_context;
198     int fd;
199 };
200 
201 static int
202 prop_one (krb5_context context, HDB *db, hdb_entry *entry, void *v)
203 {
204     krb5_error_code ret;
205     krb5_data data;
206     struct slave *slave = (struct slave *)v;
207 
208     ret = hdb_entry2value (context, entry, &data);
209     if (ret)
210 	return ret;
211     ret = krb5_data_realloc (&data, data.length + 4);
212     if (ret) {
213 	krb5_data_free (&data);
214 	return ret;
215     }
216     memmove ((char *)data.data + 4, data.data, data.length - 4);
217     _krb5_put_int (data.data, ONE_PRINC, 4);
218 
219     ret = krb5_write_priv_message (context, slave->ac, &slave->fd, &data);
220     krb5_data_free (&data);
221     return ret;
222 }
223 
224 static int
225 send_complete (krb5_context context, slave *s,
226 	       const char *database, u_int32_t current_version)
227 {
228     krb5_error_code ret;
229     HDB *db;
230     krb5_data data;
231     char buf[8];
232 
233     ret = hdb_create (context, &db, database);
234     if (ret)
235 	krb5_err (context, 1, ret, "hdb_create: %s", database);
236     ret = db->open (context, db, O_RDONLY, 0);
237     if (ret)
238 	krb5_err (context, 1, ret, "db->open");
239 
240     _krb5_put_int(buf, TELL_YOU_EVERYTHING, 4);
241 
242     data.data   = buf;
243     data.length = 4;
244 
245     ret = krb5_write_priv_message(context, s->ac, &s->fd, &data);
246 
247     if (ret)
248 	krb5_err (context, 1, ret, "krb5_write_priv_message");
249 
250     ret = hdb_foreach (context, db, 0, prop_one, s);
251     if (ret)
252 	krb5_err (context, 1, ret, "hdb_foreach");
253 
254     _krb5_put_int (buf, NOW_YOU_HAVE, 4);
255     _krb5_put_int (buf + 4, current_version, 4);
256     data.length = 8;
257 
258     ret = krb5_write_priv_message(context, s->ac, &s->fd, &data);
259 
260     if (ret)
261 	krb5_err (context, 1, ret, "krb5_write_priv_message");
262 
263     return 0;
264 }
265 
266 static int
267 send_diffs (krb5_context context, slave *s, int log_fd,
268 	    const char *database, u_int32_t current_version)
269 {
270     krb5_storage *sp;
271     u_int32_t ver;
272     time_t timestamp;
273     enum kadm_ops op;
274     u_int32_t len;
275     off_t right, left;
276     krb5_data data;
277     int ret = 0;
278 
279     if (s->version == current_version)
280 	return 0;
281 
282     sp = kadm5_log_goto_end (log_fd);
283     right = sp->seek(sp, 0, SEEK_CUR);
284     for (;;) {
285 	if (kadm5_log_previous (sp, &ver, &timestamp, &op, &len))
286 	    abort ();
287 	left = sp->seek(sp, -16, SEEK_CUR);
288 	if (ver == s->version)
289 	    return 0;
290 	if (ver == s->version + 1)
291 	    break;
292 	if (left == 0)
293 	    return send_complete (context, s, database, current_version);
294     }
295     krb5_data_alloc (&data, right - left + 4);
296     sp->fetch (sp, (char *)data.data + 4, data.length - 4);
297     krb5_storage_free(sp);
298 
299     _krb5_put_int(data.data, FOR_YOU, 4);
300 
301     ret = krb5_write_priv_message(context, s->ac, &s->fd, &data);
302 
303     if (ret) {
304 	krb5_warn (context, ret, "krb5_write_priv_message");
305 	return 1;
306     }
307     return 0;
308 }
309 
310 static int
311 process_msg (krb5_context context, slave *s, int log_fd,
312 	     const char *database, u_int32_t current_version)
313 {
314     int ret = 0;
315     krb5_data out;
316     krb5_storage *sp;
317     int32_t tmp;
318 
319     ret = krb5_read_priv_message(context, s->ac, &s->fd, &out);
320     if(ret) {
321 	krb5_warn (context, ret, "error reading message from %s", s->name);
322 	return 1;
323     }
324 
325     sp = krb5_storage_from_mem (out.data, out.length);
326     krb5_ret_int32 (sp, &tmp);
327     switch (tmp) {
328     case I_HAVE :
329 	krb5_ret_int32 (sp, &tmp);
330 	s->version = tmp;
331 	ret = send_diffs (context, s, log_fd, database, current_version);
332 	break;
333     case FOR_YOU :
334     default :
335 	krb5_warnx (context, "Ignoring command %d", tmp);
336 	break;
337     }
338 
339     krb5_data_free (&out);
340     return ret;
341 }
342 
343 static char *realm;
344 static int version_flag;
345 static int help_flag;
346 static char *keytab_str = "HDB:";
347 static char *database;
348 
349 static struct getargs args[] = {
350     { "realm", 'r', arg_string, &realm },
351     { "keytab", 'k', arg_string, &keytab_str,
352       "keytab to get authentication from", "kspec" },
353     { "database", 'd', arg_string, &database, "database", "file"},
354     { "version", 0, arg_flag, &version_flag },
355     { "help", 0, arg_flag, &help_flag }
356 };
357 static int num_args = sizeof(args) / sizeof(args[0]);
358 
359 int
360 main(int argc, char **argv)
361 {
362     krb5_error_code ret;
363     krb5_context context;
364     void *kadm_handle;
365     kadm5_server_context *server_context;
366     kadm5_config_params conf;
367     int signal_fd, listen_fd;
368     int log_fd;
369     slave *slaves = NULL;
370     u_int32_t current_version, old_version = 0;
371     krb5_keytab keytab;
372     int optind;
373 
374     optind = krb5_program_setup(&context, argc, argv, args, num_args, NULL);
375 
376     if(help_flag)
377 	krb5_std_usage(0, args, num_args);
378     if(version_flag) {
379 	print_version(NULL);
380 	exit(0);
381     }
382 
383     krb5_openlog (context, "ipropd-master", &log_facility);
384     krb5_set_warn_dest(context, log_facility);
385 
386     ret = krb5_kt_register(context, &hdb_kt_ops);
387     if(ret)
388 	krb5_err(context, 1, ret, "krb5_kt_register");
389 
390     ret = krb5_kt_resolve(context, keytab_str, &keytab);
391     if(ret)
392 	krb5_err(context, 1, ret, "krb5_kt_resolve: %s", keytab_str);
393 
394     memset(&conf, 0, sizeof(conf));
395     if(realm) {
396 	conf.mask |= KADM5_CONFIG_REALM;
397 	conf.realm = realm;
398     }
399     ret = kadm5_init_with_skey_ctx (context,
400 				    KADM5_ADMIN_SERVICE,
401 				    NULL,
402 				    KADM5_ADMIN_SERVICE,
403 				    &conf, 0, 0,
404 				    &kadm_handle);
405     if (ret)
406 	krb5_err (context, 1, ret, "kadm5_init_with_password_ctx");
407 
408     server_context = (kadm5_server_context *)kadm_handle;
409 
410     log_fd = open (server_context->log_context.log_file, O_RDONLY, 0);
411     if (log_fd < 0)
412 	krb5_err (context, 1, errno, "open %s",
413 		  server_context->log_context.log_file);
414 
415     signal_fd = make_signal_socket (context);
416     listen_fd = make_listen_socket (context);
417 
418     signal (SIGPIPE, SIG_IGN);
419 
420     for (;;) {
421 	slave *p;
422 	fd_set readset;
423 	int max_fd = 0;
424 	struct timeval to = {30, 0};
425 	u_int32_t vers;
426 
427 	if (signal_fd >= FD_SETSIZE || listen_fd >= FD_SETSIZE)
428 	    krb5_errx (context, 1, "fd too large");
429 
430 	FD_ZERO(&readset);
431 	FD_SET(signal_fd, &readset);
432 	max_fd = max(max_fd, signal_fd);
433 	FD_SET(listen_fd, &readset);
434 	max_fd = max(max_fd, listen_fd);
435 
436 	for (p = slaves; p != NULL; p = p->next) {
437 	    FD_SET(p->fd, &readset);
438 	    max_fd = max(max_fd, p->fd);
439 	}
440 
441 	ret = select (max_fd + 1,
442 		      &readset, NULL, NULL, &to);
443 	if (ret < 0) {
444 	    if (errno == EINTR)
445 		continue;
446 	    else
447 		krb5_err (context, 1, errno, "select");
448 	}
449 
450 	if (ret == 0) {
451 	    old_version = current_version;
452 	    kadm5_log_get_version_fd (log_fd, &current_version);
453 
454 	    if (current_version > old_version)
455 		for (p = slaves; p != NULL; p = p->next)
456 		    send_diffs (context, p, log_fd, database, current_version);
457 	}
458 
459 	if (ret && FD_ISSET(signal_fd, &readset)) {
460 	    struct sockaddr_un peer_addr;
461 	    socklen_t peer_len = sizeof(peer_addr);
462 
463 	    if(recvfrom(signal_fd, &vers, sizeof(vers), 0,
464 			(struct sockaddr *)&peer_addr, &peer_len) < 0) {
465 		krb5_warn (context, errno, "recvfrom");
466 		continue;
467 	    }
468 	    --ret;
469 	    old_version = current_version;
470 	    kadm5_log_get_version_fd (log_fd, &current_version);
471 	    for (p = slaves; p != NULL; p = p->next)
472 		send_diffs (context, p, log_fd, database, current_version);
473 	}
474 
475 	for(p = slaves; p != NULL; p = p->next)
476 	    if (FD_ISSET(p->fd, &readset)) {
477 		--ret;
478 		if(process_msg (context, p, log_fd, database, current_version))
479 		    remove_slave (context, p, &slaves);
480 	    }
481 
482 	if (ret && FD_ISSET(listen_fd, &readset)) {
483 	    add_slave (context, keytab, &slaves, listen_fd);
484 	    --ret;
485 	}
486 
487     }
488 
489     return 0;
490 }
491