Home | History | Annotate | Line # | Download | only in admin
      1 /*	$NetBSD: change.c,v 1.3 2023/06/19 21:41:38 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997-2005 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 "ktutil_locl.h"
     37 
     38 __RCSID("$NetBSD: change.c,v 1.3 2023/06/19 21:41:38 christos Exp $");
     39 
     40 static krb5_error_code
     41 change_entry (krb5_keytab keytab,
     42 	      krb5_principal principal, krb5_kvno kvno,
     43 	      const char *realm, const char *admin_server, int server_port)
     44 {
     45     krb5_error_code ret;
     46     kadm5_config_params conf;
     47     void *kadm_handle;
     48     char *client_name;
     49     krb5_keyblock *keys;
     50     int num_keys;
     51     int i;
     52 
     53     ret = krb5_unparse_name (context, principal, &client_name);
     54     if (ret) {
     55 	krb5_warn (context, ret, "krb5_unparse_name");
     56 	return ret;
     57     }
     58 
     59     memset (&conf, 0, sizeof(conf));
     60 
     61     if(realm == NULL)
     62 	realm = krb5_principal_get_realm(context, principal);
     63     conf.realm = strdup(realm);
     64     if (conf.realm == NULL) {
     65 	free (client_name);
     66 	krb5_set_error_message(context, ENOMEM, "malloc failed");
     67 	return ENOMEM;
     68     }
     69     conf.mask |= KADM5_CONFIG_REALM;
     70 
     71     if (admin_server) {
     72 	conf.admin_server = strdup(admin_server);
     73 	if (conf.admin_server == NULL) {
     74 	    free(client_name);
     75 	    free(conf.realm);
     76 	    krb5_set_error_message(context, ENOMEM, "malloc failed");
     77 	    return ENOMEM;
     78 	}
     79 	conf.mask |= KADM5_CONFIG_ADMIN_SERVER;
     80     }
     81 
     82     if (server_port) {
     83 	conf.kadmind_port = htons(server_port);
     84 	conf.mask |= KADM5_CONFIG_KADMIND_PORT;
     85     }
     86 
     87     ret = kadm5_init_with_skey_ctx (context,
     88 				    client_name,
     89 				    keytab_string,
     90 				    KADM5_ADMIN_SERVICE,
     91 				    &conf, 0, 0,
     92 				    &kadm_handle);
     93     free(conf.admin_server);
     94     free(conf.realm);
     95     if (ret) {
     96 	krb5_warn (context, ret,
     97 		   "kadm5_c_init_with_skey_ctx: %s:", client_name);
     98 	free (client_name);
     99 	return ret;
    100     }
    101     ret = kadm5_randkey_principal (kadm_handle, principal, &keys, &num_keys);
    102     kadm5_destroy (kadm_handle);
    103     if (ret) {
    104 	krb5_warn(context, ret, "kadm5_randkey_principal: %s:", client_name);
    105 	free (client_name);
    106 	return ret;
    107     }
    108     free (client_name);
    109     for (i = 0; i < num_keys; ++i) {
    110 	krb5_keytab_entry new_entry;
    111 
    112 	new_entry.principal = principal;
    113 	new_entry.timestamp = time (NULL);
    114 	new_entry.vno = kvno + 1;
    115 	new_entry.keyblock  = keys[i];
    116 
    117 	ret = krb5_kt_add_entry (context, keytab, &new_entry);
    118 	if (ret)
    119 	    krb5_warn (context, ret, "krb5_kt_add_entry");
    120 	krb5_free_keyblock_contents (context, &keys[i]);
    121     }
    122     return ret;
    123 }
    124 
    125 /*
    126  * loop over all the entries in the keytab (or those given) and change
    127  * their keys, writing the new keys
    128  */
    129 
    130 struct change_set {
    131     krb5_principal principal;
    132     krb5_kvno kvno;
    133 };
    134 
    135 int
    136 kt_change (struct change_options *opt, int argc, char **argv)
    137 {
    138     krb5_error_code ret;
    139     krb5_keytab keytab;
    140     krb5_kt_cursor cursor;
    141     krb5_keytab_entry entry;
    142     int i, j, max;
    143     struct change_set *changeset;
    144     int errors = 0;
    145 
    146     if((keytab = ktutil_open_keytab()) == NULL)
    147 	return 1;
    148 
    149     j = 0;
    150     max = 0;
    151     changeset = NULL;
    152 
    153     ret = krb5_kt_start_seq_get(context, keytab, &cursor);
    154     if(ret){
    155 	krb5_warn(context, ret, "%s", keytab_string);
    156 	goto out;
    157     }
    158 
    159     while((ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0) {
    160 	int add = 0;
    161 
    162 	for (i = 0; i < j; ++i) {
    163 	    if (krb5_principal_compare (context, changeset[i].principal,
    164 					entry.principal)) {
    165 		if (changeset[i].kvno < entry.vno)
    166 		    changeset[i].kvno = entry.vno;
    167 		break;
    168 	    }
    169 	}
    170 	if (i < j) {
    171 	    krb5_kt_free_entry (context, &entry);
    172 	    continue;
    173 	}
    174 
    175 	if (argc == 0) {
    176 	    add = 1;
    177 	} else {
    178 	    for (i = 0; i < argc; ++i) {
    179 		krb5_principal princ;
    180 
    181 		ret = krb5_parse_name (context, argv[i], &princ);
    182 		if (ret) {
    183 		    krb5_warn (context, ret, "%s", argv[i]);
    184 		    continue;
    185 		}
    186 		if (krb5_principal_compare (context, princ, entry.principal))
    187 		    add = 1;
    188 
    189 		krb5_free_principal (context, princ);
    190 	    }
    191 	}
    192 
    193 	if (add) {
    194 	    if (j >= max) {
    195 		void *tmp;
    196 
    197 		max = max(max * 2, 1);
    198 		tmp = realloc (changeset, max * sizeof(*changeset));
    199 		if (tmp == NULL) {
    200 		    krb5_kt_free_entry (context, &entry);
    201 		    krb5_warnx (context, "realloc: out of memory");
    202 		    ret = ENOMEM;
    203 		    break;
    204 		}
    205 		changeset = tmp;
    206 	    }
    207 	    ret = krb5_copy_principal (context, entry.principal,
    208 				       &changeset[j].principal);
    209 	    if (ret) {
    210 		krb5_warn (context, ret, "krb5_copy_principal");
    211 		krb5_kt_free_entry (context, &entry);
    212 		break;
    213 	    }
    214 	    changeset[j].kvno = entry.vno;
    215 	    ++j;
    216 	}
    217 	krb5_kt_free_entry (context, &entry);
    218     }
    219     krb5_kt_end_seq_get(context, keytab, &cursor);
    220 
    221     if (ret == KRB5_KT_END) {
    222 	for (i = 0; i < j; i++) {
    223 	    if (verbose_flag) {
    224 		char *client_name;
    225 
    226 		ret = krb5_unparse_name (context, changeset[i].principal,
    227 					 &client_name);
    228 		if (ret) {
    229 		    krb5_warn (context, ret, "krb5_unparse_name");
    230 		} else {
    231 		    printf("Changing %s kvno %d\n",
    232 			   client_name, changeset[i].kvno);
    233 		    free(client_name);
    234 		}
    235 	    }
    236 	    ret = change_entry (keytab,
    237 				changeset[i].principal, changeset[i].kvno,
    238 				opt->realm_string,
    239 				opt->admin_server_string,
    240 				opt->server_port_integer);
    241 	    if (ret != 0)
    242 		errors = 1;
    243 	}
    244     } else
    245 	errors = 1;
    246     for (i = 0; i < j; i++)
    247 	krb5_free_principal (context, changeset[i].principal);
    248     free (changeset);
    249 
    250  out:
    251     krb5_kt_close(context, keytab);
    252     return errors;
    253 }
    254