xref: /netbsd/crypto/external/bsd/heimdal/dist/kdc/misc.c (revision 1c9681d1)
1 /*	$NetBSD: misc.c,v 1.2 2017/01/28 21:31:44 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 - 2001 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 "kdc_locl.h"
37 
38 static int
name_type_ok(krb5_context context,krb5_kdc_configuration * config,krb5_const_principal principal)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
_kdc_db_fetch(krb5_context context,krb5_kdc_configuration * config,krb5_const_principal principal,unsigned flags,krb5uint32 * kvno_ptr,HDB ** db,hdb_entry_ex ** h)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
_kdc_free_ent(krb5_context context,hdb_entry_ex * ent)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
_kdc_get_preferred_key(krb5_context context,krb5_kdc_configuration * config,hdb_entry_ex * h,const char * name,krb5_enctype * enctype,Key ** key)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