xref: /freebsd/crypto/heimdal/lib/kadm5/log.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
1 /*
2  * Copyright (c) 1997, 1998, 1999 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 "kadm5_locl.h"
35 
36 RCSID("$Id: log.c,v 1.13 1999/12/04 19:50:35 assar Exp $");
37 
38 /*
39  * A log record consists of:
40  *
41  * version number		4 bytes
42  * time in seconds		4 bytes
43  * operation (enum kadm_ops)	4 bytes
44  * length of record		4 bytes
45  * data...			n bytes
46  * length of record		4 bytes
47  * version number		4 bytes
48  *
49  */
50 
51 kadm5_ret_t
52 kadm5_log_get_version (int fd,
53 		       u_int32_t *ver)
54 {
55     int ret;
56     krb5_storage *sp;
57     int32_t old_version;
58 
59     ret = lseek (fd, 0, SEEK_END);
60     if(ret < 0)
61 	return errno;
62     if(ret == 0) {
63 	*ver = 0;
64 	return 0;
65     }
66     sp = krb5_storage_from_fd (fd);
67     sp->seek(sp, -4, SEEK_CUR);
68     krb5_ret_int32 (sp, &old_version);
69     *ver = old_version;
70     krb5_storage_free(sp);
71     lseek (fd, 0, SEEK_END);
72     return 0;
73 }
74 
75 kadm5_ret_t
76 kadm5_log_init (kadm5_server_context *context)
77 {
78     int fd;
79     kadm5_ret_t ret;
80     kadm5_log_context *log_context = &context->log_context;
81 
82     if (log_context->log_fd != -1)
83 	return 0;
84     fd = open (log_context->log_file, O_RDWR | O_CREAT, 0600);
85     if (fd < 0)
86 	return errno;
87     if (flock (fd, LOCK_EX) < 0) {
88 	close (fd);
89 	return errno;
90     }
91 
92     ret = kadm5_log_get_version (fd, &log_context->version);
93     if (ret)
94 	return ret;
95 
96     log_context->log_fd  = fd;
97     return 0;
98 }
99 
100 kadm5_ret_t
101 kadm5_log_end (kadm5_server_context *context)
102 {
103     kadm5_log_context *log_context = &context->log_context;
104     int fd = log_context->log_fd;
105 
106     flock (fd, LOCK_UN);
107     close(fd);
108     log_context->log_fd = -1;
109     return 0;
110 }
111 
112 static kadm5_ret_t
113 kadm5_log_preamble (kadm5_server_context *context,
114 		    krb5_storage *sp,
115 		    enum kadm_ops op)
116 {
117     kadm5_log_context *log_context = &context->log_context;
118     kadm5_ret_t kadm_ret;
119 
120     kadm_ret = kadm5_log_init (context);
121     if (kadm_ret)
122 	return kadm_ret;
123 
124     krb5_store_int32 (sp, ++log_context->version);
125     krb5_store_int32 (sp, time(NULL));
126     krb5_store_int32 (sp, op);
127     return 0;
128 }
129 
130 static kadm5_ret_t
131 kadm5_log_postamble (kadm5_log_context *context,
132 		     krb5_storage *sp)
133 {
134     krb5_store_int32 (sp, context->version);
135     return 0;
136 }
137 
138 /*
139  * flush the log record in `sp'.
140  */
141 
142 static kadm5_ret_t
143 kadm5_log_flush (kadm5_log_context *log_context,
144 		 krb5_storage *sp)
145 {
146     krb5_data data;
147     size_t len;
148     int ret;
149 
150     krb5_storage_to_data(sp, &data);
151     len = data.length;
152     ret = write (log_context->log_fd, data.data, len);
153     if (ret != len) {
154 	krb5_data_free(&data);
155 	return errno;
156     }
157     if (fsync (log_context->log_fd) < 0) {
158 	krb5_data_free(&data);
159 	return errno;
160     }
161     /*
162      * Try to send a signal to any running `ipropd-master'
163      */
164     sendto (log_context->socket_fd,
165 	    (void *)&log_context->version,
166 	    sizeof(log_context->version),
167 	    0,
168 	    (struct sockaddr *)&log_context->socket_name,
169 	    sizeof(log_context->socket_name));
170 
171     krb5_data_free(&data);
172     return 0;
173 }
174 
175 /*
176  * Add a `create' operation to the log.
177  */
178 
179 kadm5_ret_t
180 kadm5_log_create (kadm5_server_context *context,
181 		  hdb_entry *ent)
182 {
183     krb5_storage *sp;
184     kadm5_ret_t ret;
185     krb5_data value;
186     kadm5_log_context *log_context = &context->log_context;
187 
188     sp = krb5_storage_emem();
189     ret = hdb_entry2value (context->context, ent, &value);
190     if (ret) {
191 	krb5_storage_free(sp);
192 	return ret;
193     }
194     ret = kadm5_log_preamble (context, sp, kadm_create);
195     if (ret) {
196 	krb5_data_free (&value);
197 	krb5_storage_free(sp);
198 	return ret;
199     }
200     krb5_store_int32 (sp, value.length);
201     sp->store(sp, value.data, value.length);
202     krb5_store_int32 (sp, value.length);
203     krb5_data_free (&value);
204     ret = kadm5_log_postamble (log_context, sp);
205     if (ret) {
206 	krb5_storage_free (sp);
207 	return ret;
208     }
209     ret = kadm5_log_flush (log_context, sp);
210     krb5_storage_free (sp);
211     if (ret)
212 	return ret;
213     ret = kadm5_log_end (context);
214     return ret;
215 }
216 
217 /*
218  * Read the data of a create log record from `sp' and change the
219  * database.
220  */
221 
222 kadm5_ret_t
223 kadm5_log_replay_create (kadm5_server_context *context,
224 			 u_int32_t ver,
225 			 u_int32_t len,
226 			 krb5_storage *sp)
227 {
228     krb5_error_code ret;
229     krb5_data data;
230     hdb_entry ent;
231 
232     krb5_data_alloc (&data, len);
233     sp->fetch (sp, data.data, len);
234     ret = hdb_value2entry (context->context, &data, &ent);
235     krb5_data_free(&data);
236     if (ret)
237 	return ret;
238     ret = context->db->store(context->context, context->db, 0, &ent);
239     hdb_free_entry (context->context, &ent);
240     return ret;
241 }
242 
243 /*
244  * Add a `delete' operation to the log.
245  */
246 
247 kadm5_ret_t
248 kadm5_log_delete (kadm5_server_context *context,
249 		  krb5_principal princ)
250 {
251     krb5_storage *sp;
252     kadm5_ret_t ret;
253     off_t off;
254     off_t len;
255     kadm5_log_context *log_context = &context->log_context;
256 
257     sp = krb5_storage_emem();
258     ret = kadm5_log_preamble (context, sp, kadm_delete);
259     if (ret) {
260 	krb5_storage_free(sp);
261 	return ret;
262     }
263     krb5_store_int32 (sp, 0);
264     off = sp->seek (sp, 0, SEEK_CUR);
265     krb5_store_principal (sp, princ);
266     len = sp->seek (sp, 0, SEEK_CUR) - off;
267     sp->seek(sp, -(len + 4), SEEK_CUR);
268     krb5_store_int32 (sp, len);
269     sp->seek(sp, len, SEEK_CUR);
270     krb5_store_int32 (sp, len);
271     if (ret) {
272 	krb5_storage_free (sp);
273 	return ret;
274     }
275     ret = kadm5_log_postamble (log_context, sp);
276     if (ret) {
277 	krb5_storage_free (sp);
278 	return ret;
279     }
280     ret = kadm5_log_flush (log_context, sp);
281     krb5_storage_free (sp);
282     if (ret)
283 	return ret;
284     ret = kadm5_log_end (context);
285     return ret;
286 }
287 
288 /*
289  * Read a `delete' log operation from `sp' and apply it.
290  */
291 
292 kadm5_ret_t
293 kadm5_log_replay_delete (kadm5_server_context *context,
294 			 u_int32_t ver,
295 			 u_int32_t len,
296 			 krb5_storage *sp)
297 {
298     krb5_error_code ret;
299     hdb_entry ent;
300 
301     krb5_ret_principal (sp, &ent.principal);
302 
303     ret = context->db->remove(context->context, context->db, &ent);
304     krb5_free_principal (context->context, ent.principal);
305     return ret;
306 }
307 
308 /*
309  * Add a `rename' operation to the log.
310  */
311 
312 kadm5_ret_t
313 kadm5_log_rename (kadm5_server_context *context,
314 		  krb5_principal source,
315 		  hdb_entry *ent)
316 {
317     krb5_storage *sp;
318     kadm5_ret_t ret;
319     off_t off;
320     off_t len;
321     krb5_data value;
322     kadm5_log_context *log_context = &context->log_context;
323 
324     sp = krb5_storage_emem();
325     ret = hdb_entry2value (context->context, ent, &value);
326     if (ret) {
327 	krb5_storage_free(sp);
328 	return ret;
329     }
330     ret = kadm5_log_preamble (context, sp, kadm_rename);
331     if (ret) {
332 	krb5_storage_free(sp);
333 	krb5_data_free (&value);
334 	return ret;
335     }
336     krb5_store_int32 (sp, 0);
337     off = sp->seek (sp, 0, SEEK_CUR);
338     krb5_store_principal (sp, source);
339     sp->store(sp, value.data, value.length);
340     krb5_data_free (&value);
341     len = sp->seek (sp, 0, SEEK_CUR) - off;
342 
343     sp->seek(sp, -(len + 4), SEEK_CUR);
344     krb5_store_int32 (sp, len);
345     sp->seek(sp, len, SEEK_CUR);
346     krb5_store_int32 (sp, len);
347     if (ret) {
348 	krb5_storage_free (sp);
349 	return ret;
350     }
351     ret = kadm5_log_postamble (log_context, sp);
352     if (ret) {
353 	krb5_storage_free (sp);
354 	return ret;
355     }
356     ret = kadm5_log_flush (log_context, sp);
357     krb5_storage_free (sp);
358     if (ret)
359 	return ret;
360     ret = kadm5_log_end (context);
361     return ret;
362 }
363 
364 /*
365  * Read a `rename' log operation from `sp' and apply it.
366  */
367 
368 kadm5_ret_t
369 kadm5_log_replay_rename (kadm5_server_context *context,
370 			 u_int32_t ver,
371 			 u_int32_t len,
372 			 krb5_storage *sp)
373 {
374     krb5_error_code ret;
375     krb5_principal source;
376     hdb_entry source_ent, target_ent;
377     krb5_data value;
378     off_t off;
379     size_t princ_len, data_len;
380 
381     off = sp->seek(sp, 0, SEEK_CUR);
382     krb5_ret_principal (sp, &source);
383     princ_len = sp->seek(sp, 0, SEEK_CUR) - off;
384     data_len = len - princ_len;
385     krb5_data_alloc (&value, data_len);
386     sp->fetch (sp, value.data, data_len);
387     ret = hdb_value2entry (context->context, &value, &target_ent);
388     krb5_data_free(&value);
389     if (ret) {
390 	krb5_free_principal (context->context, source);
391 	return ret;
392     }
393     ret = context->db->store (context->context, context->db, 0, &target_ent);
394     hdb_free_entry (context->context, &target_ent);
395     if (ret) {
396 	krb5_free_principal (context->context, source);
397 	return ret;
398     }
399     source_ent.principal = source;
400     ret = context->db->remove (context->context, context->db, &source_ent);
401     krb5_free_principal (context->context, source);
402     return ret;
403 }
404 
405 
406 /*
407  * Add a `modify' operation to the log.
408  */
409 
410 kadm5_ret_t
411 kadm5_log_modify (kadm5_server_context *context,
412 		  hdb_entry *ent,
413 		  u_int32_t mask)
414 {
415     krb5_storage *sp;
416     kadm5_ret_t ret;
417     krb5_data value;
418     u_int32_t len;
419     kadm5_log_context *log_context = &context->log_context;
420 
421     sp = krb5_storage_emem();
422     ret = hdb_entry2value (context->context, ent, &value);
423     if (ret) {
424 	krb5_storage_free(sp);
425 	return ret;
426     }
427     ret = kadm5_log_preamble (context, sp, kadm_modify);
428     if (ret) {
429 	krb5_data_free (&value);
430 	krb5_storage_free(sp);
431 	return ret;
432     }
433     len = value.length + 4;
434     krb5_store_int32 (sp, len);
435     krb5_store_int32 (sp, mask);
436     sp->store(sp, value.data, value.length);
437     krb5_data_free (&value);
438     krb5_store_int32 (sp, len);
439     if (ret) {
440 	krb5_storage_free (sp);
441 	return ret;
442     }
443     ret = kadm5_log_postamble (log_context, sp);
444     if (ret) {
445 	krb5_storage_free (sp);
446 	return ret;
447     }
448     ret = kadm5_log_flush (log_context, sp);
449     krb5_storage_free (sp);
450     if (ret)
451 	return ret;
452     ret = kadm5_log_end (context);
453     return ret;
454 }
455 
456 /*
457  * Read a `modify' log operation from `sp' and apply it.
458  */
459 
460 kadm5_ret_t
461 kadm5_log_replay_modify (kadm5_server_context *context,
462 			 u_int32_t ver,
463 			 u_int32_t len,
464 			 krb5_storage *sp)
465 {
466     krb5_error_code ret;
467     int32_t mask;
468     krb5_data value;
469     hdb_entry ent, log_ent;
470 
471     krb5_ret_int32 (sp, &mask);
472     len -= 4;
473     krb5_data_alloc (&value, len);
474     sp->fetch (sp, value.data, len);
475     ret = hdb_value2entry (context->context, &value, &log_ent);
476     krb5_data_free(&value);
477     if (ret)
478 	return ret;
479     ent.principal = log_ent.principal;
480     log_ent.principal = NULL;
481     ret = context->db->fetch(context->context, context->db,
482 			     HDB_F_DECRYPT, &ent);
483     if (ret)
484 	return ret;
485     if (mask & KADM5_PRINC_EXPIRE_TIME) {
486 	if (ent.valid_end == NULL)
487 	    ent.valid_end = malloc(sizeof(*ent.valid_end));
488 	*ent.valid_end = *log_ent.valid_end;
489     }
490     if (mask & KADM5_PW_EXPIRATION) {
491 	if (ent.pw_end == NULL)
492 	    ent.pw_end = malloc(sizeof(*ent.pw_end));
493 	*ent.pw_end = *log_ent.pw_end;
494     }
495     if (mask & KADM5_LAST_PWD_CHANGE) {
496 	abort ();		/* XXX */
497     }
498     if (mask & KADM5_ATTRIBUTES) {
499 	ent.flags = log_ent.flags;
500     }
501     if (mask & KADM5_MAX_LIFE) {
502 	if (ent.max_life == NULL)
503 	    ent.max_life = malloc (sizeof(*ent.max_life));
504 	*ent.max_life = *log_ent.max_life;
505     }
506     if ((mask & KADM5_MOD_TIME) && (mask & KADM5_MOD_NAME)) {
507 	if (ent.modified_by == NULL) {
508 	    ent.modified_by = malloc(sizeof(*ent.modified_by));
509 	} else
510 	    free_Event(ent.modified_by);
511 	copy_Event(log_ent.modified_by, ent.modified_by);
512     }
513     if (mask & KADM5_KVNO) {
514 	ent.kvno = log_ent.kvno;
515     }
516     if (mask & KADM5_MKVNO) {
517 	abort ();		/* XXX */
518     }
519     if (mask & KADM5_AUX_ATTRIBUTES) {
520 	abort ();		/* XXX */
521     }
522     if (mask & KADM5_POLICY) {
523 	abort ();		/* XXX */
524     }
525     if (mask & KADM5_POLICY_CLR) {
526 	abort ();		/* XXX */
527     }
528     if (mask & KADM5_MAX_RLIFE) {
529 	if (ent.max_renew == NULL)
530 	    ent.max_renew = malloc (sizeof(*ent.max_renew));
531 	*ent.max_renew = *log_ent.max_renew;
532     }
533     if (mask & KADM5_LAST_SUCCESS) {
534 	abort ();		/* XXX */
535     }
536     if (mask & KADM5_LAST_FAILED) {
537 	abort ();		/* XXX */
538     }
539     if (mask & KADM5_FAIL_AUTH_COUNT) {
540 	abort ();		/* XXX */
541     }
542     if (mask & KADM5_KEY_DATA) {
543 	size_t len;
544 	int i;
545 
546 	for (i = 0; i < ent.keys.len; ++i)
547 	    free_Key(&ent.keys.val[i]);
548 	free (ent.keys.val);
549 
550 	len = log_ent.keys.len;
551 
552 	ent.keys.len = len;
553 	ent.keys.val = malloc(len * sizeof(*ent.keys.val));
554 	for (i = 0; i < ent.keys.len; ++i)
555 	    copy_Key(&log_ent.keys.val[i],
556 		     &ent.keys.val[i]);
557     }
558     ret = context->db->store(context->context, context->db,
559 			     HDB_F_REPLACE, &ent);
560     hdb_free_entry (context->context, &ent);
561     hdb_free_entry (context->context, &log_ent);
562     return ret;
563 }
564 
565 /*
566  * Call `func' for each log record in the log in `context'
567  */
568 
569 kadm5_ret_t
570 kadm5_log_foreach (kadm5_server_context *context,
571 		   void (*func)(kadm5_server_context *server_context,
572 				u_int32_t ver,
573 				time_t timestamp,
574 				enum kadm_ops op,
575 				u_int32_t len,
576 				krb5_storage *sp))
577 {
578     int fd = context->log_context.log_fd;
579     krb5_storage *sp;
580 
581     lseek (fd, 0, SEEK_SET);
582     sp = krb5_storage_from_fd (fd);
583     for (;;) {
584 	int32_t ver, timestamp, op, len;
585 
586 	if(krb5_ret_int32 (sp, &ver) != 0)
587 	    break;
588 	krb5_ret_int32 (sp, &timestamp);
589 	krb5_ret_int32 (sp, &op);
590 	krb5_ret_int32 (sp, &len);
591 	(*func)(context, ver, timestamp, op, len, sp);
592 	sp->seek(sp, 8, SEEK_CUR);
593     }
594     return 0;
595 }
596 
597 /*
598  * Go to end of log.
599  */
600 
601 krb5_storage *
602 kadm5_log_goto_end (int fd)
603 {
604     krb5_storage *sp;
605 
606     sp = krb5_storage_from_fd (fd);
607     sp->seek(sp, 0, SEEK_END);
608     return sp;
609 }
610 
611 /*
612  * Return previous log entry.
613  */
614 
615 kadm5_ret_t
616 kadm5_log_previous (krb5_storage *sp,
617 		    u_int32_t *ver,
618 		    time_t *timestamp,
619 		    enum kadm_ops *op,
620 		    u_int32_t *len)
621 {
622     off_t off;
623     int32_t tmp;
624 
625     sp->seek(sp, -8, SEEK_CUR);
626     krb5_ret_int32 (sp, &tmp);
627     *len = tmp;
628     krb5_ret_int32 (sp, &tmp);
629     *ver = tmp;
630     off = 24 + *len;
631     sp->seek(sp, -off, SEEK_CUR);
632     krb5_ret_int32 (sp, &tmp);
633     assert(tmp == *ver);
634     krb5_ret_int32 (sp, &tmp);
635     *timestamp = tmp;
636     krb5_ret_int32 (sp, &tmp);
637     *op = tmp;
638     krb5_ret_int32 (sp, &tmp);
639     assert(tmp == *len);
640     return 0;
641 }
642 
643 /*
644  * Replay a record from the log
645  */
646 
647 kadm5_ret_t
648 kadm5_log_replay (kadm5_server_context *context,
649 		  enum kadm_ops op,
650 		  u_int32_t ver,
651 		  u_int32_t len,
652 		  krb5_storage *sp)
653 {
654     switch (op) {
655     case kadm_create :
656 	return kadm5_log_replay_create (context, ver, len, sp);
657     case kadm_delete :
658 	return kadm5_log_replay_delete (context, ver, len, sp);
659     case kadm_rename :
660 	return kadm5_log_replay_rename (context, ver, len, sp);
661     case kadm_modify :
662 	return kadm5_log_replay_modify (context, ver, len, sp);
663     default :
664 	return KADM5_FAILURE;
665     }
666 }
667