1 /*
2 * Copyright (c) 1997 - 2007 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 #include "heim_threads.h"
36
37 RCSID("$Id$");
38
39 /*
40 * A log record consists of:
41 *
42 * version number 4 bytes
43 * time in seconds 4 bytes
44 * operation (enum kadm_ops) 4 bytes
45 * length of record 4 bytes
46 * data... n bytes
47 * length of record 4 bytes
48 * version number 4 bytes
49 *
50 */
51
52 kadm5_ret_t
kadm5_log_get_version_fd(int fd,uint32_t * ver)53 kadm5_log_get_version_fd (int fd,
54 uint32_t *ver)
55 {
56 int ret;
57 krb5_storage *sp;
58 int32_t old_version;
59
60 ret = lseek (fd, 0, SEEK_END);
61 if(ret < 0)
62 return errno;
63 if(ret == 0) {
64 *ver = 0;
65 return 0;
66 }
67 sp = krb5_storage_from_fd (fd);
68 krb5_storage_seek(sp, -4, SEEK_CUR);
69 krb5_ret_int32 (sp, &old_version);
70 *ver = old_version;
71 krb5_storage_free(sp);
72 lseek (fd, 0, SEEK_END);
73 return 0;
74 }
75
76 kadm5_ret_t
kadm5_log_get_version(kadm5_server_context * context,uint32_t * ver)77 kadm5_log_get_version (kadm5_server_context *context, uint32_t *ver)
78 {
79 return kadm5_log_get_version_fd (context->log_context.log_fd, ver);
80 }
81
82 kadm5_ret_t
kadm5_log_set_version(kadm5_server_context * context,uint32_t vno)83 kadm5_log_set_version (kadm5_server_context *context, uint32_t vno)
84 {
85 kadm5_log_context *log_context = &context->log_context;
86
87 log_context->version = vno;
88 return 0;
89 }
90
91 kadm5_ret_t
kadm5_log_init(kadm5_server_context * context)92 kadm5_log_init (kadm5_server_context *context)
93 {
94 int fd;
95 kadm5_ret_t ret;
96 kadm5_log_context *log_context = &context->log_context;
97
98 if (log_context->log_fd != -1)
99 return 0;
100 fd = open (log_context->log_file, O_RDWR | O_CREAT, 0600);
101 if (fd < 0) {
102 ret = errno;
103 krb5_set_error_message(context->context, ret, "kadm5_log_init: open %s",
104 log_context->log_file);
105 return ret;
106 }
107 if (flock (fd, LOCK_EX) < 0) {
108 ret = errno;
109 krb5_set_error_message(context->context, ret, "kadm5_log_init: flock %s",
110 log_context->log_file);
111 close (fd);
112 return errno;
113 }
114
115 ret = kadm5_log_get_version_fd (fd, &log_context->version);
116 if (ret)
117 return ret;
118
119 log_context->log_fd = fd;
120 return 0;
121 }
122
123 kadm5_ret_t
kadm5_log_reinit(kadm5_server_context * context)124 kadm5_log_reinit (kadm5_server_context *context)
125 {
126 int fd;
127 kadm5_log_context *log_context = &context->log_context;
128
129 if (log_context->log_fd != -1) {
130 flock (log_context->log_fd, LOCK_UN);
131 close (log_context->log_fd);
132 log_context->log_fd = -1;
133 }
134 fd = open (log_context->log_file, O_RDWR | O_CREAT | O_TRUNC, 0600);
135 if (fd < 0)
136 return errno;
137 if (flock (fd, LOCK_EX) < 0) {
138 close (fd);
139 return errno;
140 }
141
142 log_context->version = 0;
143 log_context->log_fd = fd;
144 return 0;
145 }
146
147
148 kadm5_ret_t
kadm5_log_end(kadm5_server_context * context)149 kadm5_log_end (kadm5_server_context *context)
150 {
151 kadm5_log_context *log_context = &context->log_context;
152 int fd = log_context->log_fd;
153
154 flock (fd, LOCK_UN);
155 close(fd);
156 log_context->log_fd = -1;
157 return 0;
158 }
159
160 static kadm5_ret_t
kadm5_log_preamble(kadm5_server_context * context,krb5_storage * sp,enum kadm_ops op)161 kadm5_log_preamble (kadm5_server_context *context,
162 krb5_storage *sp,
163 enum kadm_ops op)
164 {
165 kadm5_log_context *log_context = &context->log_context;
166 kadm5_ret_t kadm_ret;
167
168 kadm_ret = kadm5_log_init (context);
169 if (kadm_ret)
170 return kadm_ret;
171
172 krb5_store_int32 (sp, ++log_context->version);
173 krb5_store_int32 (sp, time(NULL));
174 krb5_store_int32 (sp, op);
175 return 0;
176 }
177
178 static kadm5_ret_t
kadm5_log_postamble(kadm5_log_context * context,krb5_storage * sp)179 kadm5_log_postamble (kadm5_log_context *context,
180 krb5_storage *sp)
181 {
182 krb5_store_int32 (sp, context->version);
183 return 0;
184 }
185
186 /*
187 * flush the log record in `sp'.
188 */
189
190 static kadm5_ret_t
kadm5_log_flush(kadm5_log_context * log_context,krb5_storage * sp)191 kadm5_log_flush (kadm5_log_context *log_context,
192 krb5_storage *sp)
193 {
194 krb5_data data;
195 size_t len;
196 ssize_t ret;
197
198 krb5_storage_to_data(sp, &data);
199 len = data.length;
200 ret = write (log_context->log_fd, data.data, len);
201 if (ret < 0 || (size_t)ret != len) {
202 krb5_data_free(&data);
203 return errno;
204 }
205 if (fsync (log_context->log_fd) < 0) {
206 krb5_data_free(&data);
207 return errno;
208 }
209
210 /*
211 * Try to send a signal to any running `ipropd-master'
212 */
213 #ifndef NO_UNIX_SOCKETS
214 sendto (log_context->socket_fd,
215 (void *)&log_context->version,
216 sizeof(log_context->version),
217 0,
218 (struct sockaddr *)&log_context->socket_name,
219 sizeof(log_context->socket_name));
220 #else
221 sendto (log_context->socket_fd,
222 (void *)&log_context->version,
223 sizeof(log_context->version),
224 0,
225 log_context->socket_info->ai_addr,
226 log_context->socket_info->ai_addrlen);
227 #endif
228
229 krb5_data_free(&data);
230 return 0;
231 }
232
233 /*
234 * Add a `create' operation to the log.
235 */
236
237 kadm5_ret_t
kadm5_log_create(kadm5_server_context * context,hdb_entry * ent)238 kadm5_log_create (kadm5_server_context *context,
239 hdb_entry *ent)
240 {
241 krb5_storage *sp;
242 kadm5_ret_t ret;
243 krb5_data value;
244 kadm5_log_context *log_context = &context->log_context;
245
246 sp = krb5_storage_emem();
247 ret = hdb_entry2value (context->context, ent, &value);
248 if (ret) {
249 krb5_storage_free(sp);
250 return ret;
251 }
252 ret = kadm5_log_preamble (context, sp, kadm_create);
253 if (ret) {
254 krb5_data_free (&value);
255 krb5_storage_free(sp);
256 return ret;
257 }
258 krb5_store_int32 (sp, value.length);
259 krb5_storage_write(sp, value.data, value.length);
260 krb5_store_int32 (sp, value.length);
261 krb5_data_free (&value);
262 ret = kadm5_log_postamble (log_context, sp);
263 if (ret) {
264 krb5_storage_free (sp);
265 return ret;
266 }
267 ret = kadm5_log_flush (log_context, sp);
268 krb5_storage_free (sp);
269 if (ret)
270 return ret;
271 ret = kadm5_log_end (context);
272 return ret;
273 }
274
275 /*
276 * Read the data of a create log record from `sp' and change the
277 * database.
278 */
279
280 static kadm5_ret_t
kadm5_log_replay_create(kadm5_server_context * context,uint32_t ver,uint32_t len,krb5_storage * sp)281 kadm5_log_replay_create (kadm5_server_context *context,
282 uint32_t ver,
283 uint32_t len,
284 krb5_storage *sp)
285 {
286 krb5_error_code ret;
287 krb5_data data;
288 hdb_entry_ex ent;
289
290 memset(&ent, 0, sizeof(ent));
291
292 ret = krb5_data_alloc (&data, len);
293 if (ret) {
294 krb5_set_error_message(context->context, ret, "out of memory");
295 return ret;
296 }
297 krb5_storage_read (sp, data.data, len);
298 ret = hdb_value2entry (context->context, &data, &ent.entry);
299 krb5_data_free(&data);
300 if (ret) {
301 krb5_set_error_message(context->context, ret,
302 "Unmarshaling hdb entry failed");
303 return ret;
304 }
305 ret = context->db->hdb_store(context->context, context->db, 0, &ent);
306 hdb_free_entry (context->context, &ent);
307 return ret;
308 }
309
310 /*
311 * Add a `delete' operation to the log.
312 */
313
314 kadm5_ret_t
kadm5_log_delete(kadm5_server_context * context,krb5_principal princ)315 kadm5_log_delete (kadm5_server_context *context,
316 krb5_principal princ)
317 {
318 krb5_storage *sp;
319 kadm5_ret_t ret;
320 off_t off;
321 off_t len;
322 kadm5_log_context *log_context = &context->log_context;
323
324 sp = krb5_storage_emem();
325 if (sp == NULL)
326 return ENOMEM;
327 ret = kadm5_log_preamble (context, sp, kadm_delete);
328 if (ret)
329 goto out;
330 ret = krb5_store_int32 (sp, 0);
331 if (ret)
332 goto out;
333 off = krb5_storage_seek (sp, 0, SEEK_CUR);
334 ret = krb5_store_principal (sp, princ);
335 if (ret)
336 goto out;
337 len = krb5_storage_seek (sp, 0, SEEK_CUR) - off;
338 krb5_storage_seek(sp, -(len + 4), SEEK_CUR);
339 ret = krb5_store_int32 (sp, len);
340 if (ret)
341 goto out;
342 krb5_storage_seek(sp, len, SEEK_CUR);
343 ret = krb5_store_int32 (sp, len);
344 if (ret)
345 goto out;
346 ret = kadm5_log_postamble (log_context, sp);
347 if (ret)
348 goto out;
349 ret = kadm5_log_flush (log_context, sp);
350 if (ret)
351 goto out;
352 ret = kadm5_log_end (context);
353 out:
354 krb5_storage_free (sp);
355 return ret;
356 }
357
358 /*
359 * Read a `delete' log operation from `sp' and apply it.
360 */
361
362 static kadm5_ret_t
kadm5_log_replay_delete(kadm5_server_context * context,uint32_t ver,uint32_t len,krb5_storage * sp)363 kadm5_log_replay_delete (kadm5_server_context *context,
364 uint32_t ver,
365 uint32_t len,
366 krb5_storage *sp)
367 {
368 krb5_error_code ret;
369 krb5_principal principal;
370
371 ret = krb5_ret_principal (sp, &principal);
372 if (ret) {
373 krb5_set_error_message(context->context, ret, "Failed to read deleted "
374 "principal from log version: %ld", (long)ver);
375 return ret;
376 }
377
378 ret = context->db->hdb_remove(context->context, context->db, principal);
379 krb5_free_principal (context->context, principal);
380 return ret;
381 }
382
383 /*
384 * Add a `rename' operation to the log.
385 */
386
387 kadm5_ret_t
kadm5_log_rename(kadm5_server_context * context,krb5_principal source,hdb_entry * ent)388 kadm5_log_rename (kadm5_server_context *context,
389 krb5_principal source,
390 hdb_entry *ent)
391 {
392 krb5_storage *sp;
393 kadm5_ret_t ret;
394 off_t off;
395 off_t len;
396 krb5_data value;
397 kadm5_log_context *log_context = &context->log_context;
398
399 krb5_data_zero(&value);
400
401 sp = krb5_storage_emem();
402 ret = hdb_entry2value (context->context, ent, &value);
403 if (ret)
404 goto failed;
405
406 ret = kadm5_log_preamble (context, sp, kadm_rename);
407 if (ret)
408 goto failed;
409
410 ret = krb5_store_int32 (sp, 0);
411 if (ret)
412 goto failed;
413 off = krb5_storage_seek (sp, 0, SEEK_CUR);
414 ret = krb5_store_principal (sp, source);
415 if (ret)
416 goto failed;
417
418 krb5_storage_write(sp, value.data, value.length);
419 len = krb5_storage_seek (sp, 0, SEEK_CUR) - off;
420
421 krb5_storage_seek(sp, -(len + 4), SEEK_CUR);
422 ret = krb5_store_int32 (sp, len);
423 if (ret)
424 goto failed;
425
426 krb5_storage_seek(sp, len, SEEK_CUR);
427 ret = krb5_store_int32 (sp, len);
428 if (ret)
429 goto failed;
430
431 ret = kadm5_log_postamble (log_context, sp);
432 if (ret)
433 goto failed;
434
435 ret = kadm5_log_flush (log_context, sp);
436 if (ret)
437 goto failed;
438 krb5_storage_free (sp);
439 krb5_data_free (&value);
440
441 return kadm5_log_end (context);
442
443 failed:
444 krb5_data_free(&value);
445 krb5_storage_free(sp);
446 return ret;
447 }
448
449 /*
450 * Read a `rename' log operation from `sp' and apply it.
451 */
452
453 static kadm5_ret_t
kadm5_log_replay_rename(kadm5_server_context * context,uint32_t ver,uint32_t len,krb5_storage * sp)454 kadm5_log_replay_rename (kadm5_server_context *context,
455 uint32_t ver,
456 uint32_t len,
457 krb5_storage *sp)
458 {
459 krb5_error_code ret;
460 krb5_principal source;
461 hdb_entry_ex target_ent;
462 krb5_data value;
463 off_t off;
464 size_t princ_len, data_len;
465
466 memset(&target_ent, 0, sizeof(target_ent));
467
468 off = krb5_storage_seek(sp, 0, SEEK_CUR);
469 ret = krb5_ret_principal (sp, &source);
470 if (ret) {
471 krb5_set_error_message(context->context, ret, "Failed to read renamed "
472 "principal in log, version: %ld", (long)ver);
473 return ret;
474 }
475 princ_len = krb5_storage_seek(sp, 0, SEEK_CUR) - off;
476 data_len = len - princ_len;
477 ret = krb5_data_alloc (&value, data_len);
478 if (ret) {
479 krb5_free_principal (context->context, source);
480 return ret;
481 }
482 krb5_storage_read (sp, value.data, data_len);
483 ret = hdb_value2entry (context->context, &value, &target_ent.entry);
484 krb5_data_free(&value);
485 if (ret) {
486 krb5_free_principal (context->context, source);
487 return ret;
488 }
489 ret = context->db->hdb_store (context->context, context->db,
490 0, &target_ent);
491 hdb_free_entry (context->context, &target_ent);
492 if (ret) {
493 krb5_free_principal (context->context, source);
494 return ret;
495 }
496 ret = context->db->hdb_remove (context->context, context->db, source);
497 krb5_free_principal (context->context, source);
498 return ret;
499 }
500
501
502 /*
503 * Add a `modify' operation to the log.
504 */
505
506 kadm5_ret_t
kadm5_log_modify(kadm5_server_context * context,hdb_entry * ent,uint32_t mask)507 kadm5_log_modify (kadm5_server_context *context,
508 hdb_entry *ent,
509 uint32_t mask)
510 {
511 krb5_storage *sp;
512 kadm5_ret_t ret;
513 krb5_data value;
514 uint32_t len;
515 kadm5_log_context *log_context = &context->log_context;
516
517 krb5_data_zero(&value);
518
519 sp = krb5_storage_emem();
520 ret = hdb_entry2value (context->context, ent, &value);
521 if (ret)
522 goto failed;
523
524 ret = kadm5_log_preamble (context, sp, kadm_modify);
525 if (ret)
526 goto failed;
527
528 len = value.length + 4;
529 ret = krb5_store_int32 (sp, len);
530 if (ret)
531 goto failed;
532 ret = krb5_store_int32 (sp, mask);
533 if (ret)
534 goto failed;
535 krb5_storage_write (sp, value.data, value.length);
536
537 ret = krb5_store_int32 (sp, len);
538 if (ret)
539 goto failed;
540 ret = kadm5_log_postamble (log_context, sp);
541 if (ret)
542 goto failed;
543 ret = kadm5_log_flush (log_context, sp);
544 if (ret)
545 goto failed;
546 krb5_data_free(&value);
547 krb5_storage_free (sp);
548 return kadm5_log_end (context);
549 failed:
550 krb5_data_free(&value);
551 krb5_storage_free(sp);
552 return ret;
553 }
554
555 /*
556 * Read a `modify' log operation from `sp' and apply it.
557 */
558
559 static kadm5_ret_t
kadm5_log_replay_modify(kadm5_server_context * context,uint32_t ver,uint32_t len,krb5_storage * sp)560 kadm5_log_replay_modify (kadm5_server_context *context,
561 uint32_t ver,
562 uint32_t len,
563 krb5_storage *sp)
564 {
565 krb5_error_code ret;
566 int32_t mask;
567 krb5_data value;
568 hdb_entry_ex ent, log_ent;
569
570 memset(&log_ent, 0, sizeof(log_ent));
571
572 krb5_ret_int32 (sp, &mask);
573 len -= 4;
574 ret = krb5_data_alloc (&value, len);
575 if (ret) {
576 krb5_set_error_message(context->context, ret, "out of memory");
577 return ret;
578 }
579 krb5_storage_read (sp, value.data, len);
580 ret = hdb_value2entry (context->context, &value, &log_ent.entry);
581 krb5_data_free(&value);
582 if (ret)
583 return ret;
584
585 memset(&ent, 0, sizeof(ent));
586 ret = context->db->hdb_fetch_kvno(context->context, context->db,
587 log_ent.entry.principal,
588 HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
589 if (ret)
590 goto out;
591 if (mask & KADM5_PRINC_EXPIRE_TIME) {
592 if (log_ent.entry.valid_end == NULL) {
593 ent.entry.valid_end = NULL;
594 } else {
595 if (ent.entry.valid_end == NULL) {
596 ent.entry.valid_end = malloc(sizeof(*ent.entry.valid_end));
597 if (ent.entry.valid_end == NULL) {
598 ret = ENOMEM;
599 krb5_set_error_message(context->context, ret, "out of memory");
600 goto out;
601 }
602 }
603 *ent.entry.valid_end = *log_ent.entry.valid_end;
604 }
605 }
606 if (mask & KADM5_PW_EXPIRATION) {
607 if (log_ent.entry.pw_end == NULL) {
608 ent.entry.pw_end = NULL;
609 } else {
610 if (ent.entry.pw_end == NULL) {
611 ent.entry.pw_end = malloc(sizeof(*ent.entry.pw_end));
612 if (ent.entry.pw_end == NULL) {
613 ret = ENOMEM;
614 krb5_set_error_message(context->context, ret, "out of memory");
615 goto out;
616 }
617 }
618 *ent.entry.pw_end = *log_ent.entry.pw_end;
619 }
620 }
621 if (mask & KADM5_LAST_PWD_CHANGE) {
622 abort (); /* XXX */
623 }
624 if (mask & KADM5_ATTRIBUTES) {
625 ent.entry.flags = log_ent.entry.flags;
626 }
627 if (mask & KADM5_MAX_LIFE) {
628 if (log_ent.entry.max_life == NULL) {
629 ent.entry.max_life = NULL;
630 } else {
631 if (ent.entry.max_life == NULL) {
632 ent.entry.max_life = malloc (sizeof(*ent.entry.max_life));
633 if (ent.entry.max_life == NULL) {
634 ret = ENOMEM;
635 krb5_set_error_message(context->context, ret, "out of memory");
636 goto out;
637 }
638 }
639 *ent.entry.max_life = *log_ent.entry.max_life;
640 }
641 }
642 if ((mask & KADM5_MOD_TIME) && (mask & KADM5_MOD_NAME)) {
643 if (ent.entry.modified_by == NULL) {
644 ent.entry.modified_by = malloc(sizeof(*ent.entry.modified_by));
645 if (ent.entry.modified_by == NULL) {
646 ret = ENOMEM;
647 krb5_set_error_message(context->context, ret, "out of memory");
648 goto out;
649 }
650 } else
651 free_Event(ent.entry.modified_by);
652 ret = copy_Event(log_ent.entry.modified_by, ent.entry.modified_by);
653 if (ret) {
654 krb5_set_error_message(context->context, ret, "out of memory");
655 goto out;
656 }
657 }
658 if (mask & KADM5_KVNO) {
659 ent.entry.kvno = log_ent.entry.kvno;
660 }
661 if (mask & KADM5_MKVNO) {
662 abort (); /* XXX */
663 }
664 if (mask & KADM5_AUX_ATTRIBUTES) {
665 abort (); /* XXX */
666 }
667 if (mask & KADM5_POLICY) {
668 abort (); /* XXX */
669 }
670 if (mask & KADM5_POLICY_CLR) {
671 abort (); /* XXX */
672 }
673 if (mask & KADM5_MAX_RLIFE) {
674 if (log_ent.entry.max_renew == NULL) {
675 ent.entry.max_renew = NULL;
676 } else {
677 if (ent.entry.max_renew == NULL) {
678 ent.entry.max_renew = malloc (sizeof(*ent.entry.max_renew));
679 if (ent.entry.max_renew == NULL) {
680 ret = ENOMEM;
681 krb5_set_error_message(context->context, ret, "out of memory");
682 goto out;
683 }
684 }
685 *ent.entry.max_renew = *log_ent.entry.max_renew;
686 }
687 }
688 if (mask & KADM5_LAST_SUCCESS) {
689 abort (); /* XXX */
690 }
691 if (mask & KADM5_LAST_FAILED) {
692 abort (); /* XXX */
693 }
694 if (mask & KADM5_FAIL_AUTH_COUNT) {
695 abort (); /* XXX */
696 }
697 if (mask & KADM5_KEY_DATA) {
698 size_t num;
699 size_t i;
700
701 for (i = 0; i < ent.entry.keys.len; ++i)
702 free_Key(&ent.entry.keys.val[i]);
703 free (ent.entry.keys.val);
704
705 num = log_ent.entry.keys.len;
706
707 ent.entry.keys.len = num;
708 ent.entry.keys.val = malloc(len * sizeof(*ent.entry.keys.val));
709 if (ent.entry.keys.val == NULL) {
710 krb5_set_error_message(context->context, ENOMEM, "out of memory");
711 return ENOMEM;
712 }
713 for (i = 0; i < ent.entry.keys.len; ++i) {
714 ret = copy_Key(&log_ent.entry.keys.val[i],
715 &ent.entry.keys.val[i]);
716 if (ret) {
717 krb5_set_error_message(context->context, ret, "out of memory");
718 goto out;
719 }
720 }
721 }
722 if ((mask & KADM5_TL_DATA) && log_ent.entry.extensions) {
723 HDB_extensions *es = ent.entry.extensions;
724
725 ent.entry.extensions = calloc(1, sizeof(*ent.entry.extensions));
726 if (ent.entry.extensions == NULL)
727 goto out;
728
729 ret = copy_HDB_extensions(log_ent.entry.extensions,
730 ent.entry.extensions);
731 if (ret) {
732 krb5_set_error_message(context->context, ret, "out of memory");
733 free(ent.entry.extensions);
734 ent.entry.extensions = es;
735 goto out;
736 }
737 if (es) {
738 free_HDB_extensions(es);
739 free(es);
740 }
741 }
742 ret = context->db->hdb_store(context->context, context->db,
743 HDB_F_REPLACE, &ent);
744 out:
745 hdb_free_entry (context->context, &ent);
746 hdb_free_entry (context->context, &log_ent);
747 return ret;
748 }
749
750 /*
751 * Add a `nop' operation to the log. Does not close the log.
752 */
753
754 kadm5_ret_t
kadm5_log_nop(kadm5_server_context * context)755 kadm5_log_nop (kadm5_server_context *context)
756 {
757 krb5_storage *sp;
758 kadm5_ret_t ret;
759 kadm5_log_context *log_context = &context->log_context;
760
761 sp = krb5_storage_emem();
762 ret = kadm5_log_preamble (context, sp, kadm_nop);
763 if (ret) {
764 krb5_storage_free (sp);
765 return ret;
766 }
767 krb5_store_int32 (sp, 0);
768 krb5_store_int32 (sp, 0);
769 ret = kadm5_log_postamble (log_context, sp);
770 if (ret) {
771 krb5_storage_free (sp);
772 return ret;
773 }
774 ret = kadm5_log_flush (log_context, sp);
775 krb5_storage_free (sp);
776
777 return ret;
778 }
779
780 /*
781 * Read a `nop' log operation from `sp' and apply it.
782 */
783
784 static kadm5_ret_t
kadm5_log_replay_nop(kadm5_server_context * context,uint32_t ver,uint32_t len,krb5_storage * sp)785 kadm5_log_replay_nop (kadm5_server_context *context,
786 uint32_t ver,
787 uint32_t len,
788 krb5_storage *sp)
789 {
790 return 0;
791 }
792
793 /*
794 * Call `func' for each log record in the log in `context'
795 */
796
797 kadm5_ret_t
kadm5_log_foreach(kadm5_server_context * context,void (* func)(kadm5_server_context * server_context,uint32_t ver,time_t timestamp,enum kadm_ops op,uint32_t len,krb5_storage *,void *),void * ctx)798 kadm5_log_foreach (kadm5_server_context *context,
799 void (*func)(kadm5_server_context *server_context,
800 uint32_t ver,
801 time_t timestamp,
802 enum kadm_ops op,
803 uint32_t len,
804 krb5_storage *,
805 void *),
806 void *ctx)
807 {
808 int fd = context->log_context.log_fd;
809 krb5_storage *sp;
810
811 lseek (fd, 0, SEEK_SET);
812 sp = krb5_storage_from_fd (fd);
813 for (;;) {
814 int32_t ver, timestamp, op, len, len2, ver2;
815
816 if(krb5_ret_int32 (sp, &ver) != 0)
817 break;
818 krb5_ret_int32 (sp, ×tamp);
819 krb5_ret_int32 (sp, &op);
820 krb5_ret_int32 (sp, &len);
821 (*func)(context, ver, timestamp, op, len, sp, ctx);
822 krb5_ret_int32 (sp, &len2);
823 krb5_ret_int32 (sp, &ver2);
824 if (len != len2)
825 abort();
826 if (ver != ver2)
827 abort();
828 }
829 krb5_storage_free(sp);
830 return 0;
831 }
832
833 /*
834 * Go to end of log.
835 */
836
837 krb5_storage *
kadm5_log_goto_end(int fd)838 kadm5_log_goto_end (int fd)
839 {
840 krb5_storage *sp;
841
842 sp = krb5_storage_from_fd (fd);
843 krb5_storage_seek(sp, 0, SEEK_END);
844 return sp;
845 }
846
847 /*
848 * Return previous log entry.
849 *
850 * The pointer in `sp´ is assumed to be at the top of the entry before
851 * previous entry. On success, the `sp´ pointer is set to data portion
852 * of previous entry. In case of error, it's not changed at all.
853 */
854
855 kadm5_ret_t
kadm5_log_previous(krb5_context context,krb5_storage * sp,uint32_t * ver,time_t * timestamp,enum kadm_ops * op,uint32_t * len)856 kadm5_log_previous (krb5_context context,
857 krb5_storage *sp,
858 uint32_t *ver,
859 time_t *timestamp,
860 enum kadm_ops *op,
861 uint32_t *len)
862 {
863 krb5_error_code ret;
864 off_t off, oldoff;
865 int32_t tmp;
866
867 oldoff = krb5_storage_seek(sp, 0, SEEK_CUR);
868
869 krb5_storage_seek(sp, -8, SEEK_CUR);
870 ret = krb5_ret_int32 (sp, &tmp);
871 if (ret)
872 goto end_of_storage;
873 *len = tmp;
874 ret = krb5_ret_int32 (sp, &tmp);
875 if (ret)
876 goto end_of_storage;
877 *ver = tmp;
878 off = 24 + *len;
879 krb5_storage_seek(sp, -off, SEEK_CUR);
880 ret = krb5_ret_int32 (sp, &tmp);
881 if (ret)
882 goto end_of_storage;
883 if ((uint32_t)tmp != *ver) {
884 krb5_storage_seek(sp, oldoff, SEEK_SET);
885 krb5_set_error_message(context, KADM5_BAD_DB,
886 "kadm5_log_previous: log entry "
887 "have consistency failure, version number wrong "
888 "(tmp %lu ver %lu)",
889 (unsigned long)tmp,
890 (unsigned long)*ver);
891 return KADM5_BAD_DB;
892 }
893 ret = krb5_ret_int32 (sp, &tmp);
894 if (ret)
895 goto end_of_storage;
896 *timestamp = tmp;
897 ret = krb5_ret_int32 (sp, &tmp);
898 if (ret)
899 goto end_of_storage;
900 *op = tmp;
901 ret = krb5_ret_int32 (sp, &tmp);
902 if (ret)
903 goto end_of_storage;
904 if ((uint32_t)tmp != *len) {
905 krb5_storage_seek(sp, oldoff, SEEK_SET);
906 krb5_set_error_message(context, KADM5_BAD_DB,
907 "kadm5_log_previous: log entry "
908 "have consistency failure, length wrong");
909 return KADM5_BAD_DB;
910 }
911 return 0;
912
913 end_of_storage:
914 krb5_storage_seek(sp, oldoff, SEEK_SET);
915 krb5_set_error_message(context, ret, "kadm5_log_previous: end of storage "
916 "reached before end");
917 return ret;
918 }
919
920 /*
921 * Replay a record from the log
922 */
923
924 kadm5_ret_t
kadm5_log_replay(kadm5_server_context * context,enum kadm_ops op,uint32_t ver,uint32_t len,krb5_storage * sp)925 kadm5_log_replay (kadm5_server_context *context,
926 enum kadm_ops op,
927 uint32_t ver,
928 uint32_t len,
929 krb5_storage *sp)
930 {
931 switch (op) {
932 case kadm_create :
933 return kadm5_log_replay_create (context, ver, len, sp);
934 case kadm_delete :
935 return kadm5_log_replay_delete (context, ver, len, sp);
936 case kadm_rename :
937 return kadm5_log_replay_rename (context, ver, len, sp);
938 case kadm_modify :
939 return kadm5_log_replay_modify (context, ver, len, sp);
940 case kadm_nop :
941 return kadm5_log_replay_nop (context, ver, len, sp);
942 default :
943 krb5_set_error_message(context->context, KADM5_FAILURE,
944 "Unsupported replay op %d", (int)op);
945 return KADM5_FAILURE;
946 }
947 }
948
949 /*
950 * truncate the log - i.e. create an empty file with just (nop vno + 2)
951 */
952
953 kadm5_ret_t
kadm5_log_truncate(kadm5_server_context * server_context)954 kadm5_log_truncate (kadm5_server_context *server_context)
955 {
956 kadm5_ret_t ret;
957 uint32_t vno;
958
959 ret = kadm5_log_init (server_context);
960 if (ret)
961 return ret;
962
963 ret = kadm5_log_get_version (server_context, &vno);
964 if (ret)
965 return ret;
966
967 ret = kadm5_log_reinit (server_context);
968 if (ret)
969 return ret;
970
971 ret = kadm5_log_set_version (server_context, vno);
972 if (ret)
973 return ret;
974
975 ret = kadm5_log_nop (server_context);
976 if (ret)
977 return ret;
978
979 ret = kadm5_log_end (server_context);
980 if (ret)
981 return ret;
982 return 0;
983
984 }
985
986 #ifndef NO_UNIX_SOCKETS
987
988 static char *default_signal = NULL;
989 static HEIMDAL_MUTEX signal_mutex = HEIMDAL_MUTEX_INITIALIZER;
990
991 const char *
kadm5_log_signal_socket(krb5_context context)992 kadm5_log_signal_socket(krb5_context context)
993 {
994 HEIMDAL_MUTEX_lock(&signal_mutex);
995 if (!default_signal)
996 asprintf(&default_signal, "%s/signal", hdb_db_dir(context));
997 HEIMDAL_MUTEX_unlock(&signal_mutex);
998
999 return krb5_config_get_string_default(context,
1000 NULL,
1001 default_signal,
1002 "kdc",
1003 "signal_socket",
1004 NULL);
1005 }
1006
1007 #else /* NO_UNIX_SOCKETS */
1008
1009 #define SIGNAL_SOCKET_HOST "127.0.0.1"
1010 #define SIGNAL_SOCKET_PORT "12701"
1011
1012 kadm5_ret_t
kadm5_log_signal_socket_info(krb5_context context,int server_end,struct addrinfo ** ret_addrs)1013 kadm5_log_signal_socket_info(krb5_context context,
1014 int server_end,
1015 struct addrinfo **ret_addrs)
1016 {
1017 struct addrinfo hints;
1018 struct addrinfo *addrs = NULL;
1019 kadm5_ret_t ret = KADM5_FAILURE;
1020 int wsret;
1021
1022 memset(&hints, 0, sizeof(hints));
1023
1024 hints.ai_flags = AI_NUMERICHOST;
1025 if (server_end)
1026 hints.ai_flags |= AI_PASSIVE;
1027 hints.ai_family = AF_INET;
1028 hints.ai_socktype = SOCK_STREAM;
1029 hints.ai_protocol = IPPROTO_TCP;
1030
1031 wsret = getaddrinfo(SIGNAL_SOCKET_HOST,
1032 SIGNAL_SOCKET_PORT,
1033 &hints, &addrs);
1034
1035 if (wsret != 0) {
1036 krb5_set_error_message(context, KADM5_FAILURE,
1037 "%s", gai_strerror(wsret));
1038 goto done;
1039 }
1040
1041 if (addrs == NULL) {
1042 krb5_set_error_message(context, KADM5_FAILURE,
1043 "getaddrinfo() failed to return address list");
1044 goto done;
1045 }
1046
1047 *ret_addrs = addrs;
1048 addrs = NULL;
1049 ret = 0;
1050
1051 done:
1052 if (addrs)
1053 freeaddrinfo(addrs);
1054 return ret;
1055 }
1056
1057 #endif
1058