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