1 /* $NetBSD: ext.c,v 1.1.1.1 2011/04/13 18:14:41 elric Exp $ */ 2 3 /* 4 * Copyright (c) 2004 - 2005 Kungliga Tekniska Högskolan 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 "hdb_locl.h" 37 #include <krb5/der.h> 38 39 krb5_error_code 40 hdb_entry_check_mandatory(krb5_context context, const hdb_entry *ent) 41 { 42 int i; 43 44 if (ent->extensions == NULL) 45 return 0; 46 47 /* 48 * check for unknown extensions and if they where tagged mandatory 49 */ 50 51 for (i = 0; i < ent->extensions->len; i++) { 52 if (ent->extensions->val[i].data.element != 53 choice_HDB_extension_data_asn1_ellipsis) 54 continue; 55 if (ent->extensions->val[i].mandatory) { 56 krb5_set_error_message(context, HDB_ERR_MANDATORY_OPTION, 57 "Principal have unknown " 58 "mandatory extension"); 59 return HDB_ERR_MANDATORY_OPTION; 60 } 61 } 62 return 0; 63 } 64 65 HDB_extension * 66 hdb_find_extension(const hdb_entry *entry, int type) 67 { 68 int i; 69 70 if (entry->extensions == NULL) 71 return NULL; 72 73 for (i = 0; i < entry->extensions->len; i++) 74 if (entry->extensions->val[i].data.element == type) 75 return &entry->extensions->val[i]; 76 return NULL; 77 } 78 79 /* 80 * Replace the extension `ext' in `entry'. Make a copy of the 81 * extension, so the caller must still free `ext' on both success and 82 * failure. Returns 0 or error code. 83 */ 84 85 krb5_error_code 86 hdb_replace_extension(krb5_context context, 87 hdb_entry *entry, 88 const HDB_extension *ext) 89 { 90 HDB_extension *ext2; 91 HDB_extension *es; 92 int ret; 93 94 ext2 = NULL; 95 96 if (entry->extensions == NULL) { 97 entry->extensions = calloc(1, sizeof(*entry->extensions)); 98 if (entry->extensions == NULL) { 99 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 100 return ENOMEM; 101 } 102 } else if (ext->data.element != choice_HDB_extension_data_asn1_ellipsis) { 103 ext2 = hdb_find_extension(entry, ext->data.element); 104 } else { 105 /* 106 * This is an unknown extention, and we are asked to replace a 107 * possible entry in `entry' that is of the same type. This 108 * might seem impossible, but ASN.1 CHOICE comes to our 109 * rescue. The first tag in each branch in the CHOICE is 110 * unique, so just find the element in the list that have the 111 * same tag was we are putting into the list. 112 */ 113 Der_class replace_class, list_class; 114 Der_type replace_type, list_type; 115 unsigned int replace_tag, list_tag; 116 size_t size; 117 int i; 118 119 ret = der_get_tag(ext->data.u.asn1_ellipsis.data, 120 ext->data.u.asn1_ellipsis.length, 121 &replace_class, &replace_type, &replace_tag, 122 &size); 123 if (ret) { 124 krb5_set_error_message(context, ret, "hdb: failed to decode " 125 "replacement hdb extention"); 126 return ret; 127 } 128 129 for (i = 0; i < entry->extensions->len; i++) { 130 HDB_extension *ext3 = &entry->extensions->val[i]; 131 132 if (ext3->data.element != choice_HDB_extension_data_asn1_ellipsis) 133 continue; 134 135 ret = der_get_tag(ext3->data.u.asn1_ellipsis.data, 136 ext3->data.u.asn1_ellipsis.length, 137 &list_class, &list_type, &list_tag, 138 &size); 139 if (ret) { 140 krb5_set_error_message(context, ret, "hdb: failed to decode " 141 "present hdb extention"); 142 return ret; 143 } 144 145 if (MAKE_TAG(replace_class,replace_type,replace_type) == 146 MAKE_TAG(list_class,list_type,list_type)) { 147 ext2 = ext3; 148 break; 149 } 150 } 151 } 152 153 if (ext2) { 154 free_HDB_extension(ext2); 155 ret = copy_HDB_extension(ext, ext2); 156 if (ret) 157 krb5_set_error_message(context, ret, "hdb: failed to copy replacement " 158 "hdb extention"); 159 return ret; 160 } 161 162 es = realloc(entry->extensions->val, 163 (entry->extensions->len+1)*sizeof(entry->extensions->val[0])); 164 if (es == NULL) { 165 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 166 return ENOMEM; 167 } 168 entry->extensions->val = es; 169 170 ret = copy_HDB_extension(ext, 171 &entry->extensions->val[entry->extensions->len]); 172 if (ret == 0) 173 entry->extensions->len++; 174 else 175 krb5_set_error_message(context, ret, "hdb: failed to copy new extension"); 176 177 return ret; 178 } 179 180 krb5_error_code 181 hdb_clear_extension(krb5_context context, 182 hdb_entry *entry, 183 int type) 184 { 185 int i; 186 187 if (entry->extensions == NULL) 188 return 0; 189 190 for (i = 0; i < entry->extensions->len; i++) { 191 if (entry->extensions->val[i].data.element == type) { 192 free_HDB_extension(&entry->extensions->val[i]); 193 memmove(&entry->extensions->val[i], 194 &entry->extensions->val[i + 1], 195 sizeof(entry->extensions->val[i]) * (entry->extensions->len - i - 1)); 196 entry->extensions->len--; 197 } 198 } 199 if (entry->extensions->len == 0) { 200 free(entry->extensions->val); 201 free(entry->extensions); 202 entry->extensions = NULL; 203 } 204 205 return 0; 206 } 207 208 209 krb5_error_code 210 hdb_entry_get_pkinit_acl(const hdb_entry *entry, const HDB_Ext_PKINIT_acl **a) 211 { 212 const HDB_extension *ext; 213 214 ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_acl); 215 if (ext) 216 *a = &ext->data.u.pkinit_acl; 217 else 218 *a = NULL; 219 220 return 0; 221 } 222 223 krb5_error_code 224 hdb_entry_get_pkinit_hash(const hdb_entry *entry, const HDB_Ext_PKINIT_hash **a) 225 { 226 const HDB_extension *ext; 227 228 ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_cert_hash); 229 if (ext) 230 *a = &ext->data.u.pkinit_cert_hash; 231 else 232 *a = NULL; 233 234 return 0; 235 } 236 237 krb5_error_code 238 hdb_entry_get_pkinit_cert(const hdb_entry *entry, const HDB_Ext_PKINIT_cert **a) 239 { 240 const HDB_extension *ext; 241 242 ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_cert); 243 if (ext) 244 *a = &ext->data.u.pkinit_cert; 245 else 246 *a = NULL; 247 248 return 0; 249 } 250 251 krb5_error_code 252 hdb_entry_get_pw_change_time(const hdb_entry *entry, time_t *t) 253 { 254 const HDB_extension *ext; 255 256 ext = hdb_find_extension(entry, choice_HDB_extension_data_last_pw_change); 257 if (ext) 258 *t = ext->data.u.last_pw_change; 259 else 260 *t = 0; 261 262 return 0; 263 } 264 265 krb5_error_code 266 hdb_entry_set_pw_change_time(krb5_context context, 267 hdb_entry *entry, 268 time_t t) 269 { 270 HDB_extension ext; 271 272 ext.mandatory = FALSE; 273 ext.data.element = choice_HDB_extension_data_last_pw_change; 274 if (t == 0) 275 t = time(NULL); 276 ext.data.u.last_pw_change = t; 277 278 return hdb_replace_extension(context, entry, &ext); 279 } 280 281 int 282 hdb_entry_get_password(krb5_context context, HDB *db, 283 const hdb_entry *entry, char **p) 284 { 285 HDB_extension *ext; 286 char *str; 287 int ret; 288 289 ext = hdb_find_extension(entry, choice_HDB_extension_data_password); 290 if (ext) { 291 heim_utf8_string str; 292 heim_octet_string pw; 293 294 if (db->hdb_master_key_set && ext->data.u.password.mkvno) { 295 hdb_master_key key; 296 297 key = _hdb_find_master_key(ext->data.u.password.mkvno, 298 db->hdb_master_key); 299 300 if (key == NULL) { 301 krb5_set_error_message(context, HDB_ERR_NO_MKEY, 302 "master key %d missing", 303 *ext->data.u.password.mkvno); 304 return HDB_ERR_NO_MKEY; 305 } 306 307 ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY, 308 ext->data.u.password.password.data, 309 ext->data.u.password.password.length, 310 &pw); 311 } else { 312 ret = der_copy_octet_string(&ext->data.u.password.password, &pw); 313 } 314 if (ret) { 315 krb5_clear_error_message(context); 316 return ret; 317 } 318 319 str = pw.data; 320 if (str[pw.length - 1] != '\0') { 321 krb5_set_error_message(context, EINVAL, "malformed password"); 322 return EINVAL; 323 } 324 325 *p = strdup(str); 326 327 der_free_octet_string(&pw); 328 if (*p == NULL) { 329 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 330 return ENOMEM; 331 } 332 return 0; 333 } 334 335 ret = krb5_unparse_name(context, entry->principal, &str); 336 if (ret == 0) { 337 krb5_set_error_message(context, ENOENT, 338 "no password attribute for %s", str); 339 free(str); 340 } else 341 krb5_clear_error_message(context); 342 343 return ENOENT; 344 } 345 346 int 347 hdb_entry_set_password(krb5_context context, HDB *db, 348 hdb_entry *entry, const char *p) 349 { 350 HDB_extension ext; 351 hdb_master_key key; 352 int ret; 353 354 ext.mandatory = FALSE; 355 ext.data.element = choice_HDB_extension_data_password; 356 357 if (db->hdb_master_key_set) { 358 359 key = _hdb_find_master_key(NULL, db->hdb_master_key); 360 if (key == NULL) { 361 krb5_set_error_message(context, HDB_ERR_NO_MKEY, 362 "hdb_entry_set_password: " 363 "failed to find masterkey"); 364 return HDB_ERR_NO_MKEY; 365 } 366 367 ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY, 368 p, strlen(p) + 1, 369 &ext.data.u.password.password); 370 if (ret) 371 return ret; 372 373 ext.data.u.password.mkvno = 374 malloc(sizeof(*ext.data.u.password.mkvno)); 375 if (ext.data.u.password.mkvno == NULL) { 376 free_HDB_extension(&ext); 377 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 378 return ENOMEM; 379 } 380 *ext.data.u.password.mkvno = _hdb_mkey_version(key); 381 382 } else { 383 ext.data.u.password.mkvno = NULL; 384 385 ret = krb5_data_copy(&ext.data.u.password.password, 386 p, strlen(p) + 1); 387 if (ret) { 388 krb5_set_error_message(context, ret, "malloc: out of memory"); 389 free_HDB_extension(&ext); 390 return ret; 391 } 392 } 393 394 ret = hdb_replace_extension(context, entry, &ext); 395 396 free_HDB_extension(&ext); 397 398 return ret; 399 } 400 401 int 402 hdb_entry_clear_password(krb5_context context, hdb_entry *entry) 403 { 404 return hdb_clear_extension(context, entry, 405 choice_HDB_extension_data_password); 406 } 407 408 krb5_error_code 409 hdb_entry_get_ConstrainedDelegACL(const hdb_entry *entry, 410 const HDB_Ext_Constrained_delegation_acl **a) 411 { 412 const HDB_extension *ext; 413 414 ext = hdb_find_extension(entry, 415 choice_HDB_extension_data_allowed_to_delegate_to); 416 if (ext) 417 *a = &ext->data.u.allowed_to_delegate_to; 418 else 419 *a = NULL; 420 421 return 0; 422 } 423 424 krb5_error_code 425 hdb_entry_get_aliases(const hdb_entry *entry, const HDB_Ext_Aliases **a) 426 { 427 const HDB_extension *ext; 428 429 ext = hdb_find_extension(entry, choice_HDB_extension_data_aliases); 430 if (ext) 431 *a = &ext->data.u.aliases; 432 else 433 *a = NULL; 434 435 return 0; 436 } 437