xref: /freebsd/crypto/heimdal/lib/kadm5/ipropd_master.c (revision b528cefc6b8f9670b31a865051741d946cb37085)
1 /*
2  * Copyright (c) 1997, 1998 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.12 1999/12/02 17:05:06 joda Exp $");
37 
38 static int
39 make_signal_socket (krb5_context context)
40 {
41     struct sockaddr_un addr;
42     int fd;
43 
44     fd = socket (AF_UNIX, SOCK_DGRAM, 0);
45     if (fd < 0)
46 	krb5_err (context, 1, errno, "socket AF_UNIX");
47     memset (&addr, 0, sizeof(addr));
48     addr.sun_family = AF_UNIX;
49     strncpy (addr.sun_path, KADM5_LOG_SIGNAL, sizeof(addr.sun_path));
50     addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
51     unlink (addr.sun_path);
52     if (bind (fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
53 	krb5_err (context, 1, errno, "bind %s", addr.sun_path);
54     return fd;
55 }
56 
57 static int
58 make_listen_socket (krb5_context context)
59 {
60     int fd;
61     int one = 1;
62     struct sockaddr_in addr;
63 
64     fd = socket (AF_INET, SOCK_STREAM, 0);
65     if (fd < 0)
66 	krb5_err (context, 1, errno, "socket AF_INET");
67     setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
68     memset (&addr, 0, sizeof(addr));
69     addr.sin_family = AF_INET;
70     addr.sin_port   = htons(4711);
71     if(bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
72 	krb5_err (context, 1, errno, "bind");
73     if (listen(fd, SOMAXCONN) < 0)
74 	krb5_err (context, 1, errno, "listen");
75     return fd;
76 }
77 
78 struct slave {
79     int fd;
80     struct sockaddr_in addr;
81     char *name;
82     krb5_auth_context ac;
83     u_int32_t version;
84     struct slave *next;
85 };
86 
87 typedef struct slave slave;
88 
89 static int
90 check_acl (krb5_context context, const char *name)
91 {
92     FILE *fp;
93     char buf[256];
94     int ret = 1;
95 
96     fp = fopen (KADM5_SLAVE_ACL, "r");
97     if (fp == NULL)
98 	return 1;
99     while (fgets(buf, sizeof(buf), fp) != NULL) {
100 	if (buf[strlen(buf) - 1 ] == '\n')
101 	    buf[strlen(buf) - 1 ] = '\0';
102 	if (strcmp (buf, name) == 0) {
103 	    ret = 0;
104 	    break;
105 	}
106     }
107     fclose (fp);
108     return ret;
109 }
110 
111 static void
112 add_slave (krb5_context context, slave **root, int fd)
113 {
114     krb5_principal server;
115     krb5_error_code ret;
116     slave *s;
117     int addr_len;
118     krb5_ticket *ticket = NULL;
119     char hostname[128];
120 
121     s = malloc(sizeof(*s));
122     if (s == NULL) {
123 	krb5_warnx (context, "add_slave: no memory");
124 	return;
125     }
126     s->name = NULL;
127     s->ac = NULL;
128 
129     addr_len = sizeof(s->addr);
130     s->fd = accept (fd, (struct sockaddr *)&s->addr, &addr_len);
131     if (s->fd < 0) {
132 	krb5_warn (context, errno, "accept");
133 	goto error;
134     }
135     gethostname(hostname, sizeof(hostname));
136     ret = krb5_sname_to_principal (context, hostname, IPROP_NAME,
137 				   KRB5_NT_SRV_HST, &server);
138     if (ret) {
139 	krb5_warn (context, ret, "krb5_sname_to_principal");
140 	goto error;
141     }
142 
143     ret = krb5_recvauth (context, &s->ac, &s->fd,
144 			 IPROP_VERSION, server, 0, NULL, &ticket);
145     krb5_free_principal (context, server);
146     if (ret) {
147 	krb5_warn (context, ret, "krb5_recvauth");
148 	goto error;
149     }
150     ret = krb5_unparse_name (context, ticket->client, &s->name);
151     if (ret) {
152 	krb5_warn (context, ret, "krb5_unparse_name");
153 	goto error;
154     }
155     if (check_acl (context, s->name)) {
156 	krb5_warnx (context, "%s not in acl", s->name);
157 	goto error;
158     }
159     krb5_free_ticket (context, ticket);
160     printf ("connection from %s\n", s->name);
161 
162     s->version = 0;
163     s->next = *root;
164     *root = s;
165     return;
166 error:
167     if (s->name)
168 	free (s->name);
169     if (s->ac)
170 	krb5_auth_con_free(context, s->ac);
171     if (ticket)
172     krb5_free_ticket (context, ticket);
173     close (s->fd);
174     free(s);
175 }
176 
177 static void
178 remove_slave (krb5_context context, slave *s, slave **root)
179 {
180     slave **p;
181 
182     close (s->fd);
183     free (s->name);
184     krb5_auth_con_free (context, s->ac);
185 
186     for (p = root; *p; p = &(*p)->next)
187 	if (*p == s) {
188 	    *p = s->next;
189 	    break;
190 	}
191     free (s);
192 }
193 
194 static int
195 send_complete (krb5_context context, slave *s)
196 {
197     abort ();
198 }
199 
200 static int
201 send_diffs (krb5_context context, slave *s, int log_fd,
202 	    u_int32_t current_version)
203 {
204     krb5_storage *sp, *data_sp;
205     u_int32_t ver;
206     time_t timestamp;
207     enum kadm_ops op;
208     u_int32_t len;
209     off_t right, left;
210     krb5_data data;
211     krb5_data priv_data;
212     int ret = 0;
213 
214     if (s->version == current_version)
215 	return 0;
216 
217     sp = kadm5_log_goto_end (log_fd);
218     right = sp->seek(sp, 0, SEEK_CUR);
219     printf ("%ld, looking for %d\n", (long)right, s->version);
220     for (;;) {
221 	if (kadm5_log_previous (sp, &ver, &timestamp, &op, &len))
222 	    abort ();
223 	printf ("version = %d\n", ver);
224 	left = sp->seek(sp, -16, SEEK_CUR);
225 	if (ver == s->version)
226 	    return 0;
227 	if (ver == s->version + 1)
228 	    break;
229 	if (left == 0)
230 	    return send_complete (context, s);
231     }
232     krb5_data_alloc (&data, right - left + 4);
233     sp->fetch (sp, (char *)data.data + 4, data.length - 4);
234     krb5_storage_free(sp);
235 
236     _krb5_put_int(data.data, FOR_YOU, 4);
237 
238     ret = krb5_mk_priv (context, s->ac, &data, &priv_data, NULL);
239     krb5_data_free(&data);
240     if (ret) {
241 	krb5_warn (context, ret, "krb_mk_priv");
242 	return 0;
243     }
244 
245     ret = krb5_write_message (context, &s->fd, &priv_data);
246     krb5_data_free (&priv_data);
247     if (ret) {
248 	krb5_warn (context, ret, "krb5_write_message");
249 	return 1;
250     }
251     return 0;
252 }
253 
254 static int
255 process_msg (krb5_context context, slave *s, int log_fd,
256 	     u_int32_t current_version)
257 {
258     int ret = 0;
259     krb5_data in, out;
260     krb5_storage *sp;
261     int32_t tmp;
262 
263     ret = krb5_read_message (context, &s->fd, &in);
264     if (ret)
265 	return 1;
266 
267     if(in.length == 0) {
268 	krb5_warnx(context, "process_msg: short message");
269 	return 1;
270     }
271 
272     ret = krb5_rd_priv (context, s->ac, &in, &out, NULL);
273     krb5_data_free (&in);
274     if (ret) {
275 	krb5_warn (context, ret, "krb5_rd_priv");
276 	return 1;
277     }
278 
279     sp = krb5_storage_from_mem (out.data, out.length);
280     krb5_ret_int32 (sp, &tmp);
281     switch (tmp) {
282     case I_HAVE :
283 	krb5_ret_int32 (sp, &tmp);
284 	s->version = tmp;
285 	ret = send_diffs (context, s, log_fd, current_version);
286 	break;
287     case FOR_YOU :
288     default :
289 	krb5_warnx (context, "Ignoring command %d", tmp);
290 	break;
291     }
292 
293     krb5_data_free (&out);
294     return ret;
295 }
296 
297 char *realm;
298 int version_flag;
299 int help_flag;
300 struct getargs args[] = {
301     { "realm", 'r', arg_string, &realm },
302     { "version", 0, arg_flag, &version_flag },
303     { "help", 0, arg_flag, &help_flag }
304 };
305 int num_args = sizeof(args) / sizeof(args[0]);
306 
307 int
308 main(int argc, char **argv)
309 {
310     krb5_error_code ret;
311     krb5_context context;
312     void *kadm_handle;
313     kadm5_server_context *server_context;
314     kadm5_config_params conf;
315     int signal_fd, listen_fd;
316     int log_fd;
317     slave *slaves = NULL;
318     u_int32_t current_version, old_version = 0;
319 
320     int optind;
321 
322     optind = krb5_program_setup(&context, argc, argv, args, num_args, NULL);
323 
324     if(help_flag)
325 	krb5_std_usage(0, args, num_args);
326     if(version_flag) {
327 	print_version(NULL);
328 	exit(0);
329     }
330 
331     memset(&conf, 0, sizeof(conf));
332     if(realm) {
333 	conf.mask |= KADM5_CONFIG_REALM;
334 	conf.realm = realm;
335     }
336     ret = kadm5_init_with_password_ctx (context,
337 					KADM5_ADMIN_SERVICE,
338 					NULL,
339 					KADM5_ADMIN_SERVICE,
340 					&conf, 0, 0,
341 					&kadm_handle);
342     if (ret)
343 	krb5_err (context, 1, ret, "kadm5_init_with_password_ctx");
344 
345     server_context = (kadm5_server_context *)kadm_handle;
346 
347     log_fd = open (server_context->log_context.log_file, O_RDONLY, 0);
348     if (log_fd < 0)
349 	krb5_err (context, 1, errno, "open %s",
350 		  server_context->log_context.log_file);
351 
352     signal_fd = make_signal_socket (context);
353     listen_fd = make_listen_socket (context);
354 
355     for (;;) {
356 	slave *p;
357 	fd_set readset;
358 	int max_fd = 0;
359 	struct timeval to = {30, 0};
360 	u_int32_t vers;
361 
362 	FD_ZERO(&readset);
363 	FD_SET(signal_fd, &readset);
364 	max_fd = max(max_fd, signal_fd);
365 	FD_SET(listen_fd, &readset);
366 	max_fd = max(max_fd, listen_fd);
367 
368 	for (p = slaves; p != NULL; p = p->next) {
369 	    FD_SET(p->fd, &readset);
370 	    max_fd = max(max_fd, p->fd);
371 	}
372 
373 	ret = select (max_fd + 1,
374 		      &readset, NULL, NULL, &to);
375 	if (ret < 0) {
376 	    if (errno == EINTR)
377 		continue;
378 	    else
379 		krb5_err (context, 1, errno, "select");
380 	}
381 
382 	if (ret == 0) {
383 	    old_version = current_version;
384 	    kadm5_log_get_version (log_fd, &current_version);
385 
386 	    if (current_version > old_version)
387 		for (p = slaves; p != NULL; p = p->next)
388 		    send_diffs (context, p, log_fd, current_version);
389 	}
390 
391 	if (ret && FD_ISSET(signal_fd, &readset)) {
392 	    struct sockaddr_un peer_addr;
393 	    int peer_len = sizeof(peer_addr);
394 
395 	    if(recvfrom(signal_fd, &vers, sizeof(vers), 0,
396 			(struct sockaddr *)&peer_addr, &peer_len) < 0) {
397 		krb5_warn (context, errno, "recvfrom");
398 		continue;
399 	    }
400 	    printf ("signal: %u\n", vers);
401 	    --ret;
402 	    old_version = current_version;
403 	    kadm5_log_get_version (log_fd, &current_version);
404 	    for (p = slaves; p != NULL; p = p->next)
405 		send_diffs (context, p, log_fd, current_version);
406 	}
407 
408 	for(p = slaves; p != NULL && ret--; p = p->next)
409 	    if (FD_ISSET(p->fd, &readset)) {
410 		if(process_msg (context, p, log_fd, current_version))
411 		    remove_slave (context, p, &slaves);
412 	    }
413 
414 	if (ret && FD_ISSET(listen_fd, &readset)) {
415 	    add_slave (context, &slaves, listen_fd);
416 	    --ret;
417 	}
418 
419     }
420 
421     return 0;
422 }
423