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