Home | History | Annotate | Line # | Download | only in kdc
      1 /*	$NetBSD: misc.c,v 1.2 2017/01/28 21:31:44 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997 - 2001 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 "kdc_locl.h"
     37 
     38 static int
     39 name_type_ok(krb5_context context,
     40              krb5_kdc_configuration *config,
     41              krb5_const_principal principal)
     42 {
     43     int nt = krb5_principal_get_type(context, principal);
     44 
     45     if (!krb5_principal_is_krbtgt(context, principal))
     46         return 1;
     47     if (nt == KRB5_NT_SRV_INST || nt == KRB5_NT_UNKNOWN)
     48         return 1;
     49     if (config->strict_nametypes == 0)
     50         return 1;
     51     return 0;
     52 }
     53 
     54 struct timeval _kdc_now;
     55 
     56 krb5_error_code
     57 _kdc_db_fetch(krb5_context context,
     58 	      krb5_kdc_configuration *config,
     59 	      krb5_const_principal principal,
     60 	      unsigned flags,
     61 	      krb5uint32 *kvno_ptr,
     62 	      HDB **db,
     63 	      hdb_entry_ex **h)
     64 {
     65     hdb_entry_ex *ent = NULL;
     66     krb5_error_code ret = HDB_ERR_NOENTRY;
     67     int i;
     68     unsigned kvno = 0;
     69     krb5_principal enterprise_principal = NULL;
     70     krb5_const_principal princ;
     71 
     72     *h = NULL;
     73 
     74     if (!name_type_ok(context, config, principal))
     75         goto out2;
     76 
     77     if (kvno_ptr != NULL && *kvno_ptr != 0) {
     78 	kvno = *kvno_ptr;
     79 	flags |= HDB_F_KVNO_SPECIFIED;
     80     } else {
     81 	flags |= HDB_F_ALL_KVNOS;
     82     }
     83 
     84     ent = calloc(1, sizeof (*ent));
     85     if (ent == NULL)
     86         return krb5_enomem(context);
     87 
     88     if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
     89         if (principal->name.name_string.len != 1) {
     90             ret = KRB5_PARSE_MALFORMED;
     91             krb5_set_error_message(context, ret,
     92                                    "malformed request: "
     93                                    "enterprise name with %d name components",
     94                                    principal->name.name_string.len);
     95             goto out;
     96         }
     97         ret = krb5_parse_name(context, principal->name.name_string.val[0],
     98                               &enterprise_principal);
     99         if (ret)
    100             goto out;
    101     }
    102 
    103     for (i = 0; i < config->num_db; i++) {
    104 	ret = config->db[i]->hdb_open(context, config->db[i], O_RDONLY, 0);
    105 	if (ret) {
    106 	    const char *msg = krb5_get_error_message(context, ret);
    107 	    kdc_log(context, config, 0, "Failed to open database: %s", msg);
    108 	    krb5_free_error_message(context, msg);
    109 	    continue;
    110 	}
    111 
    112         princ = principal;
    113         if (!(config->db[i]->hdb_capability_flags & HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL) && enterprise_principal)
    114             princ = enterprise_principal;
    115 
    116 	ret = config->db[i]->hdb_fetch_kvno(context,
    117 					    config->db[i],
    118 					    princ,
    119 					    flags | HDB_F_DECRYPT,
    120 					    kvno,
    121 					    ent);
    122 	config->db[i]->hdb_close(context, config->db[i]);
    123 
    124 	switch (ret) {
    125 	case HDB_ERR_WRONG_REALM:
    126 	    /*
    127 	     * the ent->entry.principal just contains hints for the client
    128 	     * to retry. This is important for enterprise principal routing
    129 	     * between trusts.
    130 	     */
    131 	    /* fall through */
    132 	case 0:
    133 	    if (db)
    134 		*db = config->db[i];
    135 	    *h = ent;
    136             ent = NULL;
    137             goto out;
    138 
    139 	case HDB_ERR_NOENTRY:
    140 	    /* Check the other databases */
    141 	    continue;
    142 
    143 	default:
    144 	    /*
    145 	     * This is really important, because errors like
    146 	     * HDB_ERR_NOT_FOUND_HERE (used to indicate to Samba that
    147 	     * the RODC on which this code is running does not have
    148 	     * the key we need, and so a proxy to the KDC is required)
    149 	     * have specific meaning, and need to be propogated up.
    150 	     */
    151 	    goto out;
    152 	}
    153     }
    154 
    155 out2:
    156     if (ret == HDB_ERR_NOENTRY) {
    157 	krb5_set_error_message(context, ret, "no such entry found in hdb");
    158     }
    159 out:
    160     krb5_free_principal(context, enterprise_principal);
    161     free(ent);
    162     return ret;
    163 }
    164 
    165 void
    166 _kdc_free_ent(krb5_context context, hdb_entry_ex *ent)
    167 {
    168     hdb_free_entry (context, ent);
    169     free (ent);
    170 }
    171 
    172 /*
    173  * Use the order list of preferred encryption types and sort the
    174  * available keys and return the most preferred key.
    175  */
    176 
    177 krb5_error_code
    178 _kdc_get_preferred_key(krb5_context context,
    179 		       krb5_kdc_configuration *config,
    180 		       hdb_entry_ex *h,
    181 		       const char *name,
    182 		       krb5_enctype *enctype,
    183 		       Key **key)
    184 {
    185     krb5_error_code ret;
    186     int i;
    187 
    188     if (config->use_strongest_server_key) {
    189 	const krb5_enctype *p = krb5_kerberos_enctypes(context);
    190 
    191 	for (i = 0; p[i] != (krb5_enctype)ETYPE_NULL; i++) {
    192 	    if (krb5_enctype_valid(context, p[i]) != 0 &&
    193 		!_kdc_is_weak_exception(h->entry.principal, p[i]))
    194 		continue;
    195 	    ret = hdb_enctype2key(context, &h->entry, NULL, p[i], key);
    196 	    if (ret != 0)
    197 		continue;
    198 	    if (enctype != NULL)
    199 		*enctype = p[i];
    200 	    return 0;
    201 	}
    202     } else {
    203 	*key = NULL;
    204 
    205 	for (i = 0; i < h->entry.keys.len; i++) {
    206 	    if (krb5_enctype_valid(context, h->entry.keys.val[i].key.keytype) != 0 &&
    207 		!_kdc_is_weak_exception(h->entry.principal, h->entry.keys.val[i].key.keytype))
    208 		continue;
    209 	    ret = hdb_enctype2key(context, &h->entry, NULL,
    210 				  h->entry.keys.val[i].key.keytype, key);
    211 	    if (ret != 0)
    212 		continue;
    213 	    if (enctype != NULL)
    214 		*enctype = (*key)->key.keytype;
    215 	    return 0;
    216 	}
    217     }
    218 
    219     krb5_set_error_message(context, EINVAL,
    220 			   "No valid kerberos key found for %s", name);
    221     return EINVAL; /* XXX */
    222 }
    223 
    224