1418e2f30SShawn Emery /*
2418e2f30SShawn Emery  * CDDL HEADER START
3418e2f30SShawn Emery  *
4418e2f30SShawn Emery  * The contents of this file are subject to the terms of the
5418e2f30SShawn Emery  * Common Development and Distribution License (the "License").
6418e2f30SShawn Emery  * You may not use this file except in compliance with the License.
7418e2f30SShawn Emery  *
8418e2f30SShawn Emery  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9418e2f30SShawn Emery  * or http://www.opensolaris.org/os/licensing.
10418e2f30SShawn Emery  * See the License for the specific language governing permissions
11418e2f30SShawn Emery  * and limitations under the License.
12418e2f30SShawn Emery  *
13418e2f30SShawn Emery  * When distributing Covered Code, include this CDDL HEADER in each
14418e2f30SShawn Emery  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15418e2f30SShawn Emery  * If applicable, add the following below this CDDL HEADER, with the
16418e2f30SShawn Emery  * fields enclosed by brackets "[]" replaced with your own identifying
17418e2f30SShawn Emery  * information: Portions Copyright [yyyy] [name of copyright owner]
18418e2f30SShawn Emery  *
19418e2f30SShawn Emery  * CDDL HEADER END
20418e2f30SShawn Emery  */
21418e2f30SShawn Emery 
22418e2f30SShawn Emery /*
23418e2f30SShawn Emery  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24418e2f30SShawn Emery  */
25418e2f30SShawn Emery 
26418e2f30SShawn Emery #include <krb5.h>
27418e2f30SShawn Emery #include <errno.h>
28418e2f30SShawn Emery #include <netdb.h>
29418e2f30SShawn Emery #include <strings.h>
30418e2f30SShawn Emery #include <stdio.h>
31418e2f30SShawn Emery #include <assert.h>
32418e2f30SShawn Emery #include <ctype.h>
33418e2f30SShawn Emery #include "kt_solaris.h"
34418e2f30SShawn Emery 
35418e2f30SShawn Emery #define	AES128		ENCTYPE_AES128_CTS_HMAC_SHA1_96
36418e2f30SShawn Emery #define	AES256		ENCTYPE_AES256_CTS_HMAC_SHA1_96
37418e2f30SShawn Emery #define	DES3		ENCTYPE_DES3_CBC_SHA1
38418e2f30SShawn Emery #define	AES_ENTRIES	2
39418e2f30SShawn Emery #define	HOST_TRUNC	15
40418e2f30SShawn Emery #define	SVC_ENTRIES	4
41418e2f30SShawn Emery 
42418e2f30SShawn Emery static krb5_error_code
kt_open(krb5_context ctx,krb5_keytab * kt)43418e2f30SShawn Emery kt_open(krb5_context ctx, krb5_keytab *kt)
44418e2f30SShawn Emery {
45418e2f30SShawn Emery 	krb5_error_code code;
46418e2f30SShawn Emery 	char		buf[MAX_KEYTAB_NAME_LEN], ktstr[MAX_KEYTAB_NAME_LEN];
47418e2f30SShawn Emery 
48418e2f30SShawn Emery 	memset(buf, 0, sizeof (buf));
49418e2f30SShawn Emery 	memset(ktstr, 0, sizeof (ktstr));
50418e2f30SShawn Emery 
51418e2f30SShawn Emery 	if ((code = krb5_kt_default_name(ctx, buf, sizeof (buf))) != 0)
52418e2f30SShawn Emery 		return (code);
53418e2f30SShawn Emery 
54418e2f30SShawn Emery 	/*
55418e2f30SShawn Emery 	 * The default is file type w/o the write.  If it's anything besides
56418e2f30SShawn Emery 	 * FILE or WRFILE then we bail as quickly as possible.
57418e2f30SShawn Emery 	 */
58418e2f30SShawn Emery 	if (strncmp(buf, "FILE:", strlen("FILE:")) == 0)
59418e2f30SShawn Emery 		(void) snprintf(ktstr, sizeof (ktstr), "WR%s", buf);
60418e2f30SShawn Emery 	else if (strncmp(buf, "WRFILE:", strlen("WRFILE:")) == 0)
61418e2f30SShawn Emery 		(void) snprintf(ktstr, sizeof (ktstr), "%s", buf);
62418e2f30SShawn Emery 	else
63418e2f30SShawn Emery 		return (EINVAL);
64418e2f30SShawn Emery 
65418e2f30SShawn Emery 	return (krb5_kt_resolve(ctx, ktstr, kt));
66418e2f30SShawn Emery }
67418e2f30SShawn Emery 
68418e2f30SShawn Emery static krb5_error_code
kt_add_entry(krb5_context ctx,krb5_keytab kt,const krb5_principal princ,const krb5_principal svc_princ,krb5_enctype enctype,krb5_kvno kvno,const char * pw)69418e2f30SShawn Emery kt_add_entry(krb5_context ctx, krb5_keytab kt, const krb5_principal princ,
70418e2f30SShawn Emery     const krb5_principal svc_princ, krb5_enctype enctype, krb5_kvno kvno,
71418e2f30SShawn Emery     const char *pw)
72418e2f30SShawn Emery {
73418e2f30SShawn Emery 	krb5_keytab_entry entry;
74418e2f30SShawn Emery 	krb5_data password, salt;
75418e2f30SShawn Emery 	krb5_keyblock key;
76418e2f30SShawn Emery 	krb5_error_code code;
77418e2f30SShawn Emery 
78418e2f30SShawn Emery 	memset(&entry, 0, sizeof (entry));
79418e2f30SShawn Emery 	memset(&key, 0, sizeof (krb5_keyblock));
80418e2f30SShawn Emery 
81418e2f30SShawn Emery 	password.length = strlen(pw);
82418e2f30SShawn Emery 	password.data = (char *)pw;
83418e2f30SShawn Emery 
84418e2f30SShawn Emery 	if ((code = krb5_principal2salt(ctx, svc_princ, &salt)) != 0) {
85418e2f30SShawn Emery 		return (code);
86418e2f30SShawn Emery 	}
87418e2f30SShawn Emery 
88418e2f30SShawn Emery 	if ((krb5_c_string_to_key(ctx, enctype, &password, &salt, &key)) != 0)
89418e2f30SShawn Emery 		goto cleanup;
90418e2f30SShawn Emery 
91418e2f30SShawn Emery 	entry.key = key;
92418e2f30SShawn Emery 	entry.vno = kvno;
93418e2f30SShawn Emery 	entry.principal = princ;
94418e2f30SShawn Emery 
95418e2f30SShawn Emery 	code = krb5_kt_add_entry(ctx, kt, &entry);
96418e2f30SShawn Emery 
97418e2f30SShawn Emery cleanup:
98418e2f30SShawn Emery 
99418e2f30SShawn Emery 	krb5_xfree(salt.data);
100418e2f30SShawn Emery 	krb5_free_keyblock_contents(ctx, &key);
101418e2f30SShawn Emery 
102418e2f30SShawn Emery 	return (code);
103418e2f30SShawn Emery }
104418e2f30SShawn Emery 
105418e2f30SShawn Emery /*
106418e2f30SShawn Emery  * krb5_error_code krb5_kt_add_ad_entries(krb5_context ctx, char **sprincs_str,
107418e2f30SShawn Emery  * krb5_kvno kvno, uint_t flags, char *password)
108418e2f30SShawn Emery  *
109418e2f30SShawn Emery  * Adds keys to the keytab file for a default set of service principals in an
110418e2f30SShawn Emery  * Active Directory environment.
111418e2f30SShawn Emery  *
112418e2f30SShawn Emery  * where ctx is the pointer passed back from krb5_init_context
113418e2f30SShawn Emery  * where sprincs_str is an array of service principal names to be added
114418e2f30SShawn Emery  * to the keytab file, terminated by a NULL pointer
115418e2f30SShawn Emery  * where domain is the domain used to fully qualify the hostname for
116418e2f30SShawn Emery  * constructing the salt in the string-to-key function.
117418e2f30SShawn Emery  * where kvno is the key version number of the set of service principal
118418e2f30SShawn Emery  * keys to be added
119418e2f30SShawn Emery  * where flags is the set of conditions that affects the key table entries
120418e2f30SShawn Emery  * current set of defined flags:
121418e2f30SShawn Emery  *
122418e2f30SShawn Emery  * 	encryption type
123418e2f30SShawn Emery  * 	---------------
124418e2f30SShawn Emery  *  	0x00000001  KRB5_KT_FLAG_AES_SUPPORT (core set + AES-256-128 keys added)
125418e2f30SShawn Emery  *
126418e2f30SShawn Emery  * where password is the password that will be used to derive the key for
127418e2f30SShawn Emery  * the associated service principals in the keytab file
128418e2f30SShawn Emery  *
129418e2f30SShawn Emery  * Note: this function is used for adding service principals to the
130418e2f30SShawn Emery  * local /etc/krb5/krb5.keytab (unless KRB5_KTNAME has been set to something
131*bbf21555SRichard Lowe  * different, see krb5envvar(7)) file when the client belongs to an AD domain.
132418e2f30SShawn Emery  * The keytab file is populated differently for an AD domain as the various
133418e2f30SShawn Emery  * service principals share the same key material, unlike MIT based
134418e2f30SShawn Emery  * implementations.
135418e2f30SShawn Emery  *
136418e2f30SShawn Emery  * Note: For encryption types; the union of the enc type flag and the
137418e2f30SShawn Emery  * capabilities of the client is used to determine the enc type set to
138418e2f30SShawn Emery  * populate the keytab file.
139418e2f30SShawn Emery  *
140418e2f30SShawn Emery  * Note: The keys are not created for any AES enctypes UNLESS the
141418e2f30SShawn Emery  * KRB5_KT_FLAG_AES_SUPPORT flag is set and permitted_enctypes has the AES
142418e2f30SShawn Emery  * enctypes enabled.
143418e2f30SShawn Emery  *
144418e2f30SShawn Emery  * Note: In Active Directory environments the salt is constructed by truncating
145418e2f30SShawn Emery  * the host name to 15 characters and only use the host svc princ as the salt,
146418e2f30SShawn Emery  * e.g. host/<str15>.<domain>@<realm>.  The realm name is determined by parsing
147418e2f30SShawn Emery  * sprincs_str.  The local host name to construct is determined by calling
148418e2f30SShawn Emery  * gethostname(3C).  If AD environments construct salts differently in the
149418e2f30SShawn Emery  * future or this function is expanded outside of AD environments one could
150418e2f30SShawn Emery  * derive the salt by sending an initial authentication exchange.
151418e2f30SShawn Emery  *
152418e2f30SShawn Emery  * Note: The kvno was previously determined by performing an LDAP query of the
153418e2f30SShawn Emery  * computer account's msDS-KeyVersionNumber attribute.  If the schema changes
154418e2f30SShawn Emery  * in the future or this function is expanded outside of AD environments then
155418e2f30SShawn Emery  * one could derive the principal's kvno by requesting a service ticket.
156418e2f30SShawn Emery  */
157418e2f30SShawn Emery krb5_error_code
krb5_kt_add_ad_entries(krb5_context ctx,char ** sprincs_str,char * domain,krb5_kvno kvno,uint_t flags,char * password)158418e2f30SShawn Emery krb5_kt_add_ad_entries(krb5_context ctx, char **sprincs_str, char *domain,
159418e2f30SShawn Emery     krb5_kvno kvno, uint_t flags, char *password)
160418e2f30SShawn Emery {
161418e2f30SShawn Emery 	krb5_principal	princ = NULL, salt = NULL, f_princ = NULL;
162418e2f30SShawn Emery 	krb5_keytab	kt = NULL;
163418e2f30SShawn Emery 	krb5_enctype	*enctypes = NULL, *tenctype, penctype = 0;
164418e2f30SShawn Emery 	char		**tprinc, *ptr, *token, *t_host = NULL, *realm;
165418e2f30SShawn Emery 	char		localname[MAXHOSTNAMELEN];
166418e2f30SShawn Emery 	krb5_error_code	code;
167418e2f30SShawn Emery 	krb5_boolean	similar;
168418e2f30SShawn Emery 	uint_t		t_len;
169418e2f30SShawn Emery 
170418e2f30SShawn Emery 	assert(ctx != NULL && sprincs_str != NULL && *sprincs_str != NULL);
171418e2f30SShawn Emery 	assert(password != NULL && domain != NULL);
172418e2f30SShawn Emery 
173418e2f30SShawn Emery 	if ((code = krb5_parse_name(ctx, *sprincs_str, &f_princ)) != 0)
174418e2f30SShawn Emery 		return (code);
175418e2f30SShawn Emery 	if (krb5_princ_realm(ctx, f_princ)->length == 0) {
176418e2f30SShawn Emery 		code = EINVAL;
177418e2f30SShawn Emery 		goto cleanup;
178418e2f30SShawn Emery 	}
179418e2f30SShawn Emery 	realm = krb5_princ_realm(ctx, f_princ)->data;
180418e2f30SShawn Emery 
181418e2f30SShawn Emery 	if (gethostname(localname, MAXHOSTNAMELEN) != 0) {
182418e2f30SShawn Emery 		code = errno;
183418e2f30SShawn Emery 		goto cleanup;
184418e2f30SShawn Emery 	}
185418e2f30SShawn Emery 	token = localname;
186418e2f30SShawn Emery 
187418e2f30SShawn Emery 	/*
188418e2f30SShawn Emery 	 * Local host name could be fully qualified and/or in upper case, but
189418e2f30SShawn Emery 	 * usually and appropriately not.
190418e2f30SShawn Emery 	 */
191418e2f30SShawn Emery 	if ((ptr = strchr(token, '.')) != NULL)
192418e2f30SShawn Emery 		ptr = '\0';
193418e2f30SShawn Emery 	for (ptr = token; *ptr; ptr++)
194418e2f30SShawn Emery 		*ptr = tolower(*ptr);
195418e2f30SShawn Emery 	/*
196418e2f30SShawn Emery 	 * Windows servers currently truncate the host name to 15 characters
197418e2f30SShawn Emery 	 * and only use the host svc princ as the salt, e.g.
198418e2f30SShawn Emery 	 * host/str15.domain@realm
199418e2f30SShawn Emery 	 */
200418e2f30SShawn Emery 	t_len = snprintf(NULL, 0, "host/%.*s.%s@%s", HOST_TRUNC, token, domain,
201418e2f30SShawn Emery 	    realm) + 1;
202418e2f30SShawn Emery 	if ((t_host = malloc(t_len)) == NULL) {
203418e2f30SShawn Emery 		code = ENOMEM;
204418e2f30SShawn Emery 		goto cleanup;
205418e2f30SShawn Emery 	}
206418e2f30SShawn Emery 	(void) snprintf(t_host, t_len, "host/%.*s.%s@%s", HOST_TRUNC, token,
207418e2f30SShawn Emery 	    domain, realm);
208418e2f30SShawn Emery 
209418e2f30SShawn Emery 	if ((code = krb5_parse_name(ctx, t_host, &salt)) != 0)
210418e2f30SShawn Emery 		goto cleanup;
211418e2f30SShawn Emery 
212418e2f30SShawn Emery 	if ((code = kt_open(ctx, &kt)) != 0)
213418e2f30SShawn Emery 		goto cleanup;
214418e2f30SShawn Emery 
215418e2f30SShawn Emery 	code = krb5_get_permitted_enctypes(ctx, &enctypes);
216940daf74SToomas Soome 	if (code != 0 || *enctypes == 0)
217418e2f30SShawn Emery 		goto cleanup;
218418e2f30SShawn Emery 
219418e2f30SShawn Emery 	for (tprinc = sprincs_str; *tprinc; tprinc++) {
220418e2f30SShawn Emery 
221418e2f30SShawn Emery 		if ((code = krb5_parse_name(ctx, *tprinc, &princ)) != 0)
222418e2f30SShawn Emery 			goto cleanup;
223418e2f30SShawn Emery 
224418e2f30SShawn Emery 		for (tenctype = enctypes; *tenctype; tenctype++) {
225418e2f30SShawn Emery 			if ((!(flags & KRB5_KT_FLAG_AES_SUPPORT) &&
226418e2f30SShawn Emery 			    (*tenctype == AES128 || *tenctype == AES256)) ||
227418e2f30SShawn Emery 			    (*tenctype == DES3)) {
228418e2f30SShawn Emery 				continue;
229418e2f30SShawn Emery 			}
230418e2f30SShawn Emery 
231418e2f30SShawn Emery 			if (penctype) {
232418e2f30SShawn Emery 				code = krb5_c_enctype_compare(ctx, *tenctype,
233418e2f30SShawn Emery 				    penctype, &similar);
234418e2f30SShawn Emery 				if (code != 0)
235418e2f30SShawn Emery 					goto cleanup;
236418e2f30SShawn Emery 				else if (similar)
237418e2f30SShawn Emery 					continue;
238418e2f30SShawn Emery 			}
239418e2f30SShawn Emery 
240418e2f30SShawn Emery 			code = kt_add_entry(ctx, kt, princ, salt, *tenctype,
241418e2f30SShawn Emery 			    kvno, password);
242418e2f30SShawn Emery 			if (code != 0)
243418e2f30SShawn Emery 				goto cleanup;
244418e2f30SShawn Emery 
245418e2f30SShawn Emery 			penctype = *tenctype;
246418e2f30SShawn Emery 		}
247418e2f30SShawn Emery 
248418e2f30SShawn Emery 		krb5_free_principal(ctx, princ);
249418e2f30SShawn Emery 		princ = NULL;
250418e2f30SShawn Emery 	}
251418e2f30SShawn Emery 
252418e2f30SShawn Emery cleanup:
253418e2f30SShawn Emery 
254418e2f30SShawn Emery 	if (f_princ != NULL)
255418e2f30SShawn Emery 		krb5_free_principal(ctx, f_princ);
256418e2f30SShawn Emery 	if (salt != NULL)
257418e2f30SShawn Emery 		krb5_free_principal(ctx, salt);
258418e2f30SShawn Emery 	if (t_host != NULL)
259418e2f30SShawn Emery 		free(t_host);
260418e2f30SShawn Emery 	if (kt != NULL)
261418e2f30SShawn Emery 		(void) krb5_kt_close(ctx, kt);
262418e2f30SShawn Emery 	if (enctypes != NULL)
263418e2f30SShawn Emery 		krb5_free_ktypes(ctx, enctypes);
264418e2f30SShawn Emery 	if (princ != NULL)
265418e2f30SShawn Emery 		krb5_free_principal(ctx, princ);
266418e2f30SShawn Emery 
267418e2f30SShawn Emery 	return (code);
268418e2f30SShawn Emery }
269418e2f30SShawn Emery 
270418e2f30SShawn Emery #define	PRINCIPAL	0
271418e2f30SShawn Emery #define	REALM		1
272418e2f30SShawn Emery 
273418e2f30SShawn Emery static krb5_error_code
kt_remove_by_key(krb5_context ctx,char * key,uint_t type)274418e2f30SShawn Emery kt_remove_by_key(krb5_context ctx, char *key, uint_t type)
275418e2f30SShawn Emery {
276418e2f30SShawn Emery 	krb5_error_code		code;
277418e2f30SShawn Emery 	krb5_kt_cursor		cursor;
278418e2f30SShawn Emery 	krb5_keytab_entry	entry;
279418e2f30SShawn Emery 	krb5_keytab		kt = NULL;
280418e2f30SShawn Emery 	krb5_principal		svc_princ = NULL;
281418e2f30SShawn Emery 	krb5_principal_data	realm_data;
282418e2f30SShawn Emery 	boolean_t		found = FALSE;
283418e2f30SShawn Emery 
284418e2f30SShawn Emery 	assert(ctx != NULL && key != NULL);
285418e2f30SShawn Emery 
286418e2f30SShawn Emery 	if (type == REALM) {
287418e2f30SShawn Emery 		krb5_princ_realm(ctx, &realm_data)->length = strlen(key);
288418e2f30SShawn Emery 		krb5_princ_realm(ctx, &realm_data)->data = key;
289418e2f30SShawn Emery 	} else if (type == PRINCIPAL) {
290418e2f30SShawn Emery 		if ((code = krb5_parse_name(ctx, key, &svc_princ)) != 0)
291418e2f30SShawn Emery 			goto cleanup;
292418e2f30SShawn Emery 	} else
293418e2f30SShawn Emery 		return (EINVAL);
294418e2f30SShawn Emery 
295418e2f30SShawn Emery 	if ((code = kt_open(ctx, &kt)) != 0)
296418e2f30SShawn Emery 		goto cleanup;
297418e2f30SShawn Emery 
298418e2f30SShawn Emery 	if ((code = krb5_kt_start_seq_get(ctx, kt, &cursor)) != 0)
299418e2f30SShawn Emery 		goto cleanup;
300418e2f30SShawn Emery 
301418e2f30SShawn Emery 	while ((code = krb5_kt_next_entry(ctx, kt, &entry, &cursor)) == 0) {
302418e2f30SShawn Emery 		if (type == PRINCIPAL && krb5_principal_compare(ctx, svc_princ,
303418e2f30SShawn Emery 		    entry.principal)) {
304418e2f30SShawn Emery 			found = TRUE;
305418e2f30SShawn Emery 		} else if (type == REALM && krb5_realm_compare(ctx, &realm_data,
306418e2f30SShawn Emery 		    entry.principal)) {
307418e2f30SShawn Emery 			found = TRUE;
308418e2f30SShawn Emery 		}
309418e2f30SShawn Emery 
310418e2f30SShawn Emery 		if (found == TRUE) {
311418e2f30SShawn Emery 			code = krb5_kt_end_seq_get(ctx, kt, &cursor);
312418e2f30SShawn Emery 			if (code != 0) {
313418e2f30SShawn Emery 				krb5_kt_free_entry(ctx, &entry);
314418e2f30SShawn Emery 				goto cleanup;
315418e2f30SShawn Emery 			}
316418e2f30SShawn Emery 
317418e2f30SShawn Emery 			code = krb5_kt_remove_entry(ctx, kt, &entry);
318418e2f30SShawn Emery 			if (code != 0) {
319418e2f30SShawn Emery 				krb5_kt_free_entry(ctx, &entry);
320418e2f30SShawn Emery 				goto cleanup;
321418e2f30SShawn Emery 			}
322418e2f30SShawn Emery 
323418e2f30SShawn Emery 			code = krb5_kt_start_seq_get(ctx, kt, &cursor);
324418e2f30SShawn Emery 			if (code != 0) {
325418e2f30SShawn Emery 				krb5_kt_free_entry(ctx, &entry);
326418e2f30SShawn Emery 				goto cleanup;
327418e2f30SShawn Emery 			}
328418e2f30SShawn Emery 
329418e2f30SShawn Emery 			found = FALSE;
330418e2f30SShawn Emery 		}
331418e2f30SShawn Emery 
332418e2f30SShawn Emery 		krb5_kt_free_entry(ctx, &entry);
333418e2f30SShawn Emery 	}
334418e2f30SShawn Emery 
335418e2f30SShawn Emery 	if (code && code != KRB5_KT_END)
336418e2f30SShawn Emery 		goto cleanup;
337418e2f30SShawn Emery 
338418e2f30SShawn Emery 	code = krb5_kt_end_seq_get(ctx, kt, &cursor);
339418e2f30SShawn Emery 
340418e2f30SShawn Emery cleanup:
341418e2f30SShawn Emery 
342418e2f30SShawn Emery 	if (svc_princ != NULL)
343418e2f30SShawn Emery 		krb5_free_principal(ctx, svc_princ);
344418e2f30SShawn Emery 	if (kt != NULL)
345418e2f30SShawn Emery 		(void) krb5_kt_close(ctx, kt);
346418e2f30SShawn Emery 
347418e2f30SShawn Emery 	return (code);
348418e2f30SShawn Emery }
349418e2f30SShawn Emery 
350418e2f30SShawn Emery /*
351418e2f30SShawn Emery  * krb5_error_code krb5_kt_remove_by_realm(krb5_context ctx, char *realm)
352418e2f30SShawn Emery  *
353418e2f30SShawn Emery  * Removes all key entries in the keytab file that match the exact realm name
354418e2f30SShawn Emery  * specified.
355418e2f30SShawn Emery  *
356418e2f30SShawn Emery  * where ctx is the pointer passed back from krb5_init_context
357418e2f30SShawn Emery  * where realm is the realm name that is matched for any keytab entries
358418e2f30SShawn Emery  * to be removed
359418e2f30SShawn Emery  *
360418e2f30SShawn Emery  * Note: if there are no entries matching realm then 0 (success) is returned
361418e2f30SShawn Emery  */
362418e2f30SShawn Emery krb5_error_code
krb5_kt_remove_by_realm(krb5_context ctx,char * realm)363418e2f30SShawn Emery krb5_kt_remove_by_realm(krb5_context ctx, char *realm)
364418e2f30SShawn Emery {
365418e2f30SShawn Emery 
366418e2f30SShawn Emery 	return (kt_remove_by_key(ctx, realm, REALM));
367418e2f30SShawn Emery }
368418e2f30SShawn Emery 
369418e2f30SShawn Emery /*
370418e2f30SShawn Emery  * krb5_error_code krb5_kt_remove_by_svcprinc(krb5_context ctx,
371418e2f30SShawn Emery  *	char *sprinc_str)
372418e2f30SShawn Emery  *
373418e2f30SShawn Emery  * Removes all key entries in the keytab file that match the exact service
374418e2f30SShawn Emery  * principal name specified.
375418e2f30SShawn Emery  *
376418e2f30SShawn Emery  * where ctx is the pointer passed back from krb5_init_context
377418e2f30SShawn Emery  * where sprinc_str is the service principal name that is matched for any
378418e2f30SShawn Emery  * keytab entries to be removed
379418e2f30SShawn Emery  *
380418e2f30SShawn Emery  * Note: if there are no entries matching sprinc_str then 0 (success) is
381418e2f30SShawn Emery  * returned
382418e2f30SShawn Emery  */
383418e2f30SShawn Emery krb5_error_code
krb5_kt_remove_by_svcprinc(krb5_context ctx,char * sprinc_str)384418e2f30SShawn Emery krb5_kt_remove_by_svcprinc(krb5_context ctx, char *sprinc_str)
385418e2f30SShawn Emery {
386418e2f30SShawn Emery 
387418e2f30SShawn Emery 	return (kt_remove_by_key(ctx, sprinc_str, PRINCIPAL));
388418e2f30SShawn Emery }
389418e2f30SShawn Emery 
390418e2f30SShawn Emery /*
391418e2f30SShawn Emery  * krb5_error_code krb5_kt_validate(krb5_context ctx, char *sprinc_str,
392418e2f30SShawn Emery  * uint_t flags, boolean_t *valid)
393418e2f30SShawn Emery  *
394418e2f30SShawn Emery  * The validate function determines that the service principal exists and that
395418e2f30SShawn Emery  * it has a valid set of encryption types for said principal.
396418e2f30SShawn Emery  *
397418e2f30SShawn Emery  * where ctx is the pointer passed back from krb5_init_context
398418e2f30SShawn Emery  * where sprinc_str is the principal to be validated in the keytab file
399418e2f30SShawn Emery  * where flags is the set of conditions that affects the key table entries
400418e2f30SShawn Emery  * that the function considers valid
401418e2f30SShawn Emery  * 	current set of defined flags:
402418e2f30SShawn Emery  *
403418e2f30SShawn Emery  *	encryption type
404418e2f30SShawn Emery  *	---------------
405418e2f30SShawn Emery  *	0x00000001 KRB5_KT_FLAG_AES_SUPPORT (core set + AES-256-128 keys are
406418e2f30SShawn Emery  *		valid)
407418e2f30SShawn Emery  *
408418e2f30SShawn Emery  * where valid is a boolean that is set if the sprinc_str is correctly
409418e2f30SShawn Emery  * populated in the keytab file based on the flags set else valid is unset.
410418e2f30SShawn Emery  *
411418e2f30SShawn Emery  * Note: The validate function assumes that only one set of keys exists for
412418e2f30SShawn Emery  * a corresponding service principal, of key version number (kvno) n.  It would
413418e2f30SShawn Emery  * consider more than one kvno set as invalid.  This is from the fact that AD
414418e2f30SShawn Emery  * clients will attempt to refresh credential caches if KRB5KRB_AP_ERR_MODIFIED
415418e2f30SShawn Emery  * is returned by the acceptor when the requested kvno is not found within the
416418e2f30SShawn Emery  * keytab file.
417418e2f30SShawn Emery  */
418418e2f30SShawn Emery krb5_error_code
krb5_kt_ad_validate(krb5_context ctx,char * sprinc_str,uint_t flags,boolean_t * valid)419418e2f30SShawn Emery krb5_kt_ad_validate(krb5_context ctx, char *sprinc_str, uint_t flags,
420418e2f30SShawn Emery     boolean_t *valid)
421418e2f30SShawn Emery {
422418e2f30SShawn Emery 	krb5_error_code		code;
423418e2f30SShawn Emery 	krb5_kt_cursor		cursor;
424418e2f30SShawn Emery 	krb5_keytab_entry	entry;
425418e2f30SShawn Emery 	krb5_keytab		kt = NULL;
426418e2f30SShawn Emery 	krb5_principal		svc_princ = NULL;
427418e2f30SShawn Emery 	krb5_enctype		*enctypes, *tenctype, penctype = 0;
428418e2f30SShawn Emery 	boolean_t		ck_aes = FALSE;
429418e2f30SShawn Emery 	uint_t			aes_count = 0, kt_entries = 0;
430418e2f30SShawn Emery 	krb5_boolean		similar;
431418e2f30SShawn Emery 
432418e2f30SShawn Emery 	assert(ctx != NULL && sprinc_str != NULL && valid != NULL);
433418e2f30SShawn Emery 
434418e2f30SShawn Emery 	*valid = FALSE;
435418e2f30SShawn Emery 	ck_aes = flags & KRB5_KT_FLAG_AES_SUPPORT;
436418e2f30SShawn Emery 
437418e2f30SShawn Emery 	if ((code = krb5_parse_name(ctx, sprinc_str, &svc_princ)) != 0)
438418e2f30SShawn Emery 		goto cleanup;
439418e2f30SShawn Emery 
440418e2f30SShawn Emery 	if ((code = kt_open(ctx, &kt)) != 0)
441418e2f30SShawn Emery 		goto cleanup;
442418e2f30SShawn Emery 
443418e2f30SShawn Emery 	code = krb5_get_permitted_enctypes(ctx, &enctypes);
444940daf74SToomas Soome 	if (code != 0 || *enctypes == 0)
445418e2f30SShawn Emery 		goto cleanup;
446418e2f30SShawn Emery 
447418e2f30SShawn Emery 	if ((code = krb5_kt_start_seq_get(ctx, kt, &cursor)) != 0)
448418e2f30SShawn Emery 		goto cleanup;
449418e2f30SShawn Emery 
450418e2f30SShawn Emery 	while ((code = krb5_kt_next_entry(ctx, kt, &entry, &cursor)) == 0) {
451418e2f30SShawn Emery 		if (krb5_principal_compare(ctx, svc_princ, entry.principal)) {
452418e2f30SShawn Emery 
453418e2f30SShawn Emery 			for (tenctype = enctypes; *tenctype; tenctype++) {
454418e2f30SShawn Emery 				if (penctype) {
455418e2f30SShawn Emery 					code = krb5_c_enctype_compare(ctx,
456418e2f30SShawn Emery 					    *tenctype, penctype, &similar);
457418e2f30SShawn Emery 					if (code != 0) {
458418e2f30SShawn Emery 						krb5_kt_free_entry(ctx, &entry);
459418e2f30SShawn Emery 						goto cleanup;
460418e2f30SShawn Emery 					} else if (similar)
461418e2f30SShawn Emery 						continue;
462418e2f30SShawn Emery 				}
463418e2f30SShawn Emery 
464418e2f30SShawn Emery 				if ((*tenctype != DES3) &&
465418e2f30SShawn Emery 				    (entry.key.enctype == *tenctype)) {
466418e2f30SShawn Emery 					kt_entries++;
467418e2f30SShawn Emery 				}
468418e2f30SShawn Emery 
469418e2f30SShawn Emery 				penctype = *tenctype;
470418e2f30SShawn Emery 			}
471418e2f30SShawn Emery 
472418e2f30SShawn Emery 			if ((entry.key.enctype == AES128) ||
473418e2f30SShawn Emery 			    (entry.key.enctype == AES256)) {
474418e2f30SShawn Emery 				aes_count++;
475418e2f30SShawn Emery 			}
476418e2f30SShawn Emery 		}
477418e2f30SShawn Emery 
478418e2f30SShawn Emery 		krb5_kt_free_entry(ctx, &entry);
479418e2f30SShawn Emery 	}
480418e2f30SShawn Emery 
481418e2f30SShawn Emery 	if (code && code != KRB5_KT_END)
482418e2f30SShawn Emery 		goto cleanup;
483418e2f30SShawn Emery 
484418e2f30SShawn Emery 	if ((code = krb5_kt_end_seq_get(ctx, kt, &cursor)))
485418e2f30SShawn Emery 		goto cleanup;
486418e2f30SShawn Emery 
487418e2f30SShawn Emery 	if (ck_aes == TRUE) {
488418e2f30SShawn Emery 		if ((kt_entries != SVC_ENTRIES) || (aes_count != AES_ENTRIES))
489418e2f30SShawn Emery 			goto cleanup;
490418e2f30SShawn Emery 	} else if (kt_entries != (SVC_ENTRIES - AES_ENTRIES))
491418e2f30SShawn Emery 		goto cleanup;
492418e2f30SShawn Emery 
493418e2f30SShawn Emery 	*valid = TRUE;
494418e2f30SShawn Emery 
495418e2f30SShawn Emery cleanup:
496418e2f30SShawn Emery 
497418e2f30SShawn Emery 	if (svc_princ != NULL)
498418e2f30SShawn Emery 		krb5_free_principal(ctx, svc_princ);
499418e2f30SShawn Emery 	if (kt != NULL)
500418e2f30SShawn Emery 		(void) krb5_kt_close(ctx, kt);
501418e2f30SShawn Emery 	if (enctypes != NULL)
502418e2f30SShawn Emery 		krb5_free_ktypes(ctx, enctypes);
503418e2f30SShawn Emery 
504418e2f30SShawn Emery 	return (code);
505418e2f30SShawn Emery }
506