1da6c28aaSamw /*
2da6c28aaSamw  * CDDL HEADER START
3da6c28aaSamw  *
4da6c28aaSamw  * The contents of this file are subject to the terms of the
5da6c28aaSamw  * Common Development and Distribution License (the "License").
6da6c28aaSamw  * You may not use this file except in compliance with the License.
7da6c28aaSamw  *
8da6c28aaSamw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aaSamw  * or http://www.opensolaris.org/os/licensing.
10da6c28aaSamw  * See the License for the specific language governing permissions
11da6c28aaSamw  * and limitations under the License.
12da6c28aaSamw  *
13da6c28aaSamw  * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aaSamw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aaSamw  * If applicable, add the following below this CDDL HEADER, with the
16da6c28aaSamw  * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aaSamw  * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aaSamw  *
19da6c28aaSamw  * CDDL HEADER END
20da6c28aaSamw  */
21da6c28aaSamw 
22da6c28aaSamw /*
23148c5f43SAlan Wright  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
2412b65585SGordon Ross  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
25da6c28aaSamw  */
26da6c28aaSamw 
27da6c28aaSamw #include <stdio.h>
28da6c28aaSamw #include <stdlib.h>
29da6c28aaSamw #include <string.h>
30da6c28aaSamw #include <strings.h>
31da6c28aaSamw #include <unistd.h>
32da6c28aaSamw #include <ctype.h>
33da6c28aaSamw #include <errno.h>
34da6c28aaSamw #include <syslog.h>
35faa1795aSjb #include <netdb.h>
36faa1795aSjb #include <sys/param.h>
37da6c28aaSamw #include <kerberosv5/krb5.h>
3855bf511dSas #include <kerberosv5/com_err.h>
39faa1795aSjb 
40faa1795aSjb #include <smbsrv/libsmb.h>
4155bf511dSas #include <smbns_krb.h>
42da6c28aaSamw 
43faa1795aSjb /*
44148c5f43SAlan Wright  * Kerberized services available on the system.
45faa1795aSjb  */
46148c5f43SAlan Wright static smb_krb5_pn_t smb_krb5_pn_tab[] = {
47148c5f43SAlan Wright 	/*
48148c5f43SAlan Wright 	 * Service keys are salted with the SMB_KRB_PN_ID_ID_SALT prinipal
49148c5f43SAlan Wright 	 * name.
50148c5f43SAlan Wright 	 */
51148c5f43SAlan Wright 	{SMB_KRB5_PN_ID_SALT,		SMB_PN_SVC_HOST,	SMB_PN_SALT},
52faa1795aSjb 
5312b65585SGordon Ross 	/* CIFS SPNs. (HOST, CIFS, ...) */
54148c5f43SAlan Wright 	{SMB_KRB5_PN_ID_HOST_FQHN,	SMB_PN_SVC_HOST,
55148c5f43SAlan Wright 	    SMB_PN_KEYTAB_ENTRY | SMB_PN_SPN_ATTR | SMB_PN_UPN_ATTR},
5612b65585SGordon Ross 	{SMB_KRB5_PN_ID_HOST_SHORT,	SMB_PN_SVC_HOST,
5712b65585SGordon Ross 	    SMB_PN_KEYTAB_ENTRY | SMB_PN_SPN_ATTR},
5812b65585SGordon Ross 	{SMB_KRB5_PN_ID_CIFS_FQHN,	SMB_PN_SVC_CIFS,
5912b65585SGordon Ross 	    SMB_PN_KEYTAB_ENTRY | SMB_PN_SPN_ATTR},
6012b65585SGordon Ross 	{SMB_KRB5_PN_ID_CIFS_SHORT,	SMB_PN_SVC_CIFS,
6112b65585SGordon Ross 	    SMB_PN_KEYTAB_ENTRY | SMB_PN_SPN_ATTR},
6212b65585SGordon Ross 	{SMB_KRB5_PN_ID_MACHINE,	NULL,
6312b65585SGordon Ross 	    SMB_PN_KEYTAB_ENTRY},
64faa1795aSjb 
65148c5f43SAlan Wright 	/* NFS */
66148c5f43SAlan Wright 	{SMB_KRB5_PN_ID_NFS_FQHN,	SMB_PN_SVC_NFS,
67148c5f43SAlan Wright 	    SMB_PN_KEYTAB_ENTRY | SMB_PN_SPN_ATTR},
68faa1795aSjb 
69148c5f43SAlan Wright 	/* HTTP */
70148c5f43SAlan Wright 	{SMB_KRB5_PN_ID_HTTP_FQHN,	SMB_PN_SVC_HTTP,
71148c5f43SAlan Wright 	    SMB_PN_KEYTAB_ENTRY | SMB_PN_SPN_ATTR},
72faa1795aSjb 
73148c5f43SAlan Wright 	/* ROOT */
74148c5f43SAlan Wright 	{SMB_KRB5_PN_ID_ROOT_FQHN,	SMB_PN_SVC_ROOT,
75148c5f43SAlan Wright 	    SMB_PN_KEYTAB_ENTRY | SMB_PN_SPN_ATTR},
76148c5f43SAlan Wright };
77148c5f43SAlan Wright 
78148c5f43SAlan Wright #define	SMB_KRB5_SPN_TAB_SZ \
79148c5f43SAlan Wright 	(sizeof (smb_krb5_pn_tab) / sizeof (smb_krb5_pn_tab[0]))
80148c5f43SAlan Wright 
81148c5f43SAlan Wright #define	SMB_KRB5_MAX_BUFLEN	128
82148c5f43SAlan Wright 
83148c5f43SAlan Wright static int smb_krb5_kt_open(krb5_context, char *, krb5_keytab *);
84148c5f43SAlan Wright static int smb_krb5_kt_addkey(krb5_context, krb5_keytab, const krb5_principal,
85148c5f43SAlan Wright     krb5_enctype, krb5_kvno, const krb5_data *, const char *);
86148c5f43SAlan Wright static int smb_krb5_spn_count(uint32_t);
87148c5f43SAlan Wright static smb_krb5_pn_t *smb_krb5_lookup_pn(smb_krb5_pn_id_t);
88148c5f43SAlan Wright static char *smb_krb5_get_pn_by_id(smb_krb5_pn_id_t, uint32_t,
89148c5f43SAlan Wright     const char *);
90148c5f43SAlan Wright static int smb_krb5_get_kprinc(krb5_context, smb_krb5_pn_id_t, uint32_t,
91148c5f43SAlan Wright     const char *, krb5_principal *);
92faa1795aSjb 
93faa1795aSjb 
94faa1795aSjb /*
95148c5f43SAlan Wright  * Generates a null-terminated array of principal names that
96148c5f43SAlan Wright  * represents the list of the available Kerberized services
97148c5f43SAlan Wright  * of the specified type (SPN attribute, UPN attribute, or
98148c5f43SAlan Wright  * keytab entry).
99148c5f43SAlan Wright  *
100148c5f43SAlan Wright  * Returns the number of principal names returned via the 1st
101148c5f43SAlan Wright  * output parameter (i.e. vals).
102faa1795aSjb  *
103148c5f43SAlan Wright  * Caller must invoke smb_krb5_free_spns to free the allocated
104148c5f43SAlan Wright  * memory when finished.
105faa1795aSjb  */
106148c5f43SAlan Wright uint32_t
smb_krb5_get_pn_set(smb_krb5_pn_set_t * set,uint32_t type,char * fqdn)107148c5f43SAlan Wright smb_krb5_get_pn_set(smb_krb5_pn_set_t *set, uint32_t type, char *fqdn)
108faa1795aSjb {
109148c5f43SAlan Wright 	int cnt, i;
110148c5f43SAlan Wright 	smb_krb5_pn_t *tabent;
111faa1795aSjb 
112148c5f43SAlan Wright 	if (!set || !fqdn)
113148c5f43SAlan Wright 		return (0);
114faa1795aSjb 
115148c5f43SAlan Wright 	bzero(set, sizeof (smb_krb5_pn_set_t));
116148c5f43SAlan Wright 	cnt = smb_krb5_spn_count(type);
117148c5f43SAlan Wright 	set->s_pns = (char **)calloc(cnt + 1, sizeof (char *));
118faa1795aSjb 
119148c5f43SAlan Wright 	if (set->s_pns == NULL)
120148c5f43SAlan Wright 		return (0);
121faa1795aSjb 
122148c5f43SAlan Wright 	for (i = 0, set->s_cnt = 0; i < SMB_KRB5_SPN_TAB_SZ; i++) {
123148c5f43SAlan Wright 		tabent = &smb_krb5_pn_tab[i];
124faa1795aSjb 
125148c5f43SAlan Wright 		if (set->s_cnt == cnt)
126148c5f43SAlan Wright 			break;
127faa1795aSjb 
128148c5f43SAlan Wright 		if ((tabent->p_flags & type) != type)
129148c5f43SAlan Wright 			continue;
130faa1795aSjb 
131148c5f43SAlan Wright 		set->s_pns[set->s_cnt] = smb_krb5_get_pn_by_id(tabent->p_id,
132148c5f43SAlan Wright 		    type, fqdn);
133148c5f43SAlan Wright 		if (set->s_pns[set->s_cnt] == NULL) {
134148c5f43SAlan Wright 			syslog(LOG_ERR, "smbns_ksetpwd: failed to obtain "
135148c5f43SAlan Wright 			    "principal names: possible transient memory "
136148c5f43SAlan Wright 			    "shortage");
137148c5f43SAlan Wright 			smb_krb5_free_pn_set(set);
138148c5f43SAlan Wright 			return (0);
139148c5f43SAlan Wright 		}
140faa1795aSjb 
141148c5f43SAlan Wright 		set->s_cnt++;
142148c5f43SAlan Wright 	}
143faa1795aSjb 
144148c5f43SAlan Wright 	if (set->s_cnt == 0)
145148c5f43SAlan Wright 		smb_krb5_free_pn_set(set);
146faa1795aSjb 
147148c5f43SAlan Wright 	return (set->s_cnt);
148148c5f43SAlan Wright }
149faa1795aSjb 
150148c5f43SAlan Wright void
smb_krb5_free_pn_set(smb_krb5_pn_set_t * set)151148c5f43SAlan Wright smb_krb5_free_pn_set(smb_krb5_pn_set_t *set)
152148c5f43SAlan Wright {
153148c5f43SAlan Wright 	int i;
154faa1795aSjb 
155148c5f43SAlan Wright 	if (set == NULL || set->s_pns == NULL)
156148c5f43SAlan Wright 		return;
157faa1795aSjb 
158148c5f43SAlan Wright 	for (i = 0; i < set->s_cnt; i++)
159148c5f43SAlan Wright 		free(set->s_pns[i]);
160148c5f43SAlan Wright 
161148c5f43SAlan Wright 	free(set->s_pns);
162148c5f43SAlan Wright 	set->s_pns = NULL;
163faa1795aSjb }
164da6c28aaSamw 
165da6c28aaSamw /*
166da6c28aaSamw  * Initialize the kerberos context.
167da6c28aaSamw  * Return 0 on success. Otherwise, return -1.
168da6c28aaSamw  */
169da6c28aaSamw int
smb_krb5_ctx_init(krb5_context * ctx)170da6c28aaSamw smb_krb5_ctx_init(krb5_context *ctx)
171da6c28aaSamw {
172da6c28aaSamw 	if (krb5_init_context(ctx) != 0)
173da6c28aaSamw 		return (-1);
174da6c28aaSamw 
175da6c28aaSamw 	return (0);
176da6c28aaSamw }
177da6c28aaSamw 
178da6c28aaSamw /*
179148c5f43SAlan Wright  * Free the kerberos context.
180148c5f43SAlan Wright  */
181148c5f43SAlan Wright void
smb_krb5_ctx_fini(krb5_context ctx)182148c5f43SAlan Wright smb_krb5_ctx_fini(krb5_context ctx)
183148c5f43SAlan Wright {
184148c5f43SAlan Wright 	krb5_free_context(ctx);
185148c5f43SAlan Wright }
186148c5f43SAlan Wright 
187148c5f43SAlan Wright /*
188148c5f43SAlan Wright  * Create an array of Kerberos Princiapls given an array of principal names.
189148c5f43SAlan Wright  * Caller must free the allocated memory using smb_krb5_free_kprincs()
190148c5f43SAlan Wright  * upon success.
191da6c28aaSamw  *
192148c5f43SAlan Wright  * Returns 0 on success. Otherwise, returns -1.
193da6c28aaSamw  */
194da6c28aaSamw int
smb_krb5_get_kprincs(krb5_context ctx,char ** names,size_t num,krb5_principal ** krb5princs)195148c5f43SAlan Wright smb_krb5_get_kprincs(krb5_context ctx, char **names, size_t num,
196148c5f43SAlan Wright     krb5_principal **krb5princs)
197da6c28aaSamw {
198faa1795aSjb 	int i;
199faa1795aSjb 
200148c5f43SAlan Wright 	if ((*krb5princs = calloc(num, sizeof (krb5_principal *))) == NULL) {
201148c5f43SAlan Wright 		return (-1);
202148c5f43SAlan Wright 	}
203faa1795aSjb 
204148c5f43SAlan Wright 	for (i = 0; i < num; i++) {
205148c5f43SAlan Wright 		if (krb5_parse_name(ctx, names[i], &(*krb5princs)[i]) != 0) {
206148c5f43SAlan Wright 			smb_krb5_free_kprincs(ctx, *krb5princs, i);
207faa1795aSjb 			return (-1);
208faa1795aSjb 		}
209faa1795aSjb 	}
210148c5f43SAlan Wright 
211da6c28aaSamw 	return (0);
212da6c28aaSamw }
213da6c28aaSamw 
214faa1795aSjb void
smb_krb5_free_kprincs(krb5_context ctx,krb5_principal * krb5princs,size_t num)215148c5f43SAlan Wright smb_krb5_free_kprincs(krb5_context ctx, krb5_principal *krb5princs,
216faa1795aSjb     size_t num)
217faa1795aSjb {
218faa1795aSjb 	int i;
219faa1795aSjb 
220faa1795aSjb 	for (i = 0; i < num; i++)
221faa1795aSjb 		krb5_free_principal(ctx, krb5princs[i]);
222faa1795aSjb 
223148c5f43SAlan Wright 	free(krb5princs);
224da6c28aaSamw }
225da6c28aaSamw 
226da6c28aaSamw /*
227da6c28aaSamw  * Set the workstation trust account password.
228da6c28aaSamw  * Returns 0 on success.  Otherwise, returns non-zero value.
229da6c28aaSamw  */
230da6c28aaSamw int
smb_krb5_setpwd(krb5_context ctx,const char * fqdn,char * passwd)231148c5f43SAlan Wright smb_krb5_setpwd(krb5_context ctx, const char *fqdn, char *passwd)
232da6c28aaSamw {
233da6c28aaSamw 	krb5_error_code code;
234da6c28aaSamw 	krb5_ccache cc = NULL;
235148c5f43SAlan Wright 	int result_code = 0;
236da6c28aaSamw 	krb5_data result_code_string, result_string;
237148c5f43SAlan Wright 	krb5_principal princ;
238148c5f43SAlan Wright 	char msg[SMB_KRB5_MAX_BUFLEN];
239148c5f43SAlan Wright 
240148c5f43SAlan Wright 	if (smb_krb5_get_kprinc(ctx, SMB_KRB5_PN_ID_HOST_FQHN,
241148c5f43SAlan Wright 	    SMB_PN_UPN_ATTR, fqdn, &princ) != 0)
242148c5f43SAlan Wright 		return (-1);
243da6c28aaSamw 
244da6c28aaSamw 	(void) memset(&result_code_string, 0, sizeof (result_code_string));
245da6c28aaSamw 	(void) memset(&result_string, 0, sizeof (result_string));
246da6c28aaSamw 
247da6c28aaSamw 	if ((code = krb5_cc_default(ctx, &cc)) != 0) {
248148c5f43SAlan Wright 		(void) snprintf(msg, sizeof (msg), "smbns_ksetpwd: failed to "
249148c5f43SAlan Wright 		    "find %s", SMB_CCACHE_PATH);
250148c5f43SAlan Wright 		smb_krb5_log_errmsg(ctx, msg, code);
251148c5f43SAlan Wright 		krb5_free_principal(ctx, princ);
252da6c28aaSamw 		return (-1);
253da6c28aaSamw 	}
254da6c28aaSamw 
255da6c28aaSamw 	code = krb5_set_password_using_ccache(ctx, cc, passwd, princ,
256da6c28aaSamw 	    &result_code, &result_code_string, &result_string);
257da6c28aaSamw 
258*b3700b07SGordon Ross 	(void) krb5_cc_close(ctx, cc);
259*b3700b07SGordon Ross 
260148c5f43SAlan Wright 	if (code != 0)
261148c5f43SAlan Wright 		smb_krb5_log_errmsg(ctx, "smbns_ksetpwd: KPASSWD protocol "
262148c5f43SAlan Wright 		    "exchange failed", code);
263148c5f43SAlan Wright 
264*b3700b07SGordon Ross 	if (result_code != 0) {
265*b3700b07SGordon Ross 		syslog(LOG_ERR, "smbns_ksetpwd: KPASSWD failed: rc=%d %.*s",
266*b3700b07SGordon Ross 		    result_code,
267*b3700b07SGordon Ross 		    result_code_string.length,
268148c5f43SAlan Wright 		    result_code_string.data);
269*b3700b07SGordon Ross 		if (code == 0)
270*b3700b07SGordon Ross 			code = EACCES;
271*b3700b07SGordon Ross 	}
272cbfb650aScp 
273148c5f43SAlan Wright 	krb5_free_principal(ctx, princ);
274cbfb650aScp 	free(result_code_string.data);
275cbfb650aScp 	free(result_string.data);
276da6c28aaSamw 	return (code);
277da6c28aaSamw }
278da6c28aaSamw 
279da6c28aaSamw /*
28055bf511dSas  * Open the keytab file for writing.
28155bf511dSas  * The keytab should be closed by calling krb5_kt_close().
282da6c28aaSamw  */
28355bf511dSas static int
smb_krb5_kt_open(krb5_context ctx,char * fname,krb5_keytab * kt)284148c5f43SAlan Wright smb_krb5_kt_open(krb5_context ctx, char *fname, krb5_keytab *kt)
285da6c28aaSamw {
286da6c28aaSamw 	char *ktname;
287148c5f43SAlan Wright 	krb5_error_code code;
28855bf511dSas 	int len;
289148c5f43SAlan Wright 	char msg[SMB_KRB5_MAX_BUFLEN];
290da6c28aaSamw 
29155bf511dSas 	*kt = NULL;
292da6c28aaSamw 	len = snprintf(NULL, 0, "WRFILE:%s", fname) + 1;
293da6c28aaSamw 	if ((ktname = malloc(len)) == NULL) {
294148c5f43SAlan Wright 		syslog(LOG_ERR, "smbns_ksetpwd: unable to open keytab %s: "
295148c5f43SAlan Wright 		    "possible transient memory shortage", fname);
296da6c28aaSamw 		return (-1);
297da6c28aaSamw 	}
298da6c28aaSamw 
299da6c28aaSamw 	(void) snprintf(ktname, len, "WRFILE:%s", fname);
300da6c28aaSamw 
301148c5f43SAlan Wright 	if ((code = krb5_kt_resolve(ctx, ktname, kt)) != 0) {
302148c5f43SAlan Wright 		(void) snprintf(msg, sizeof (msg), "smbns_ksetpwd: %s", fname);
303148c5f43SAlan Wright 		smb_krb5_log_errmsg(ctx, msg, code);
304da6c28aaSamw 		free(ktname);
305da6c28aaSamw 		return (-1);
306da6c28aaSamw 	}
307da6c28aaSamw 
308da6c28aaSamw 	free(ktname);
30955bf511dSas 	return (0);
31055bf511dSas }
31155bf511dSas 
31255bf511dSas /*
313148c5f43SAlan Wright  * Populate the keytab with keys of the specified key version for the
314148c5f43SAlan Wright  * specified set of krb5 principals.  All service keys will be salted by:
315148c5f43SAlan Wright  * host/<truncated@15_lower_case_hostname>.<fqdn>@<REALM>
31655bf511dSas  */
31755bf511dSas int
smb_krb5_kt_populate(krb5_context ctx,const char * fqdn,krb5_principal * princs,int count,char * fname,krb5_kvno kvno,char * passwd,krb5_enctype * enctypes,int enctype_count)318148c5f43SAlan Wright smb_krb5_kt_populate(krb5_context ctx, const char *fqdn,
319148c5f43SAlan Wright     krb5_principal *princs, int count, char *fname, krb5_kvno kvno,
320148c5f43SAlan Wright     char *passwd, krb5_enctype *enctypes, int enctype_count)
32155bf511dSas {
32255bf511dSas 	krb5_keytab kt = NULL;
323148c5f43SAlan Wright 	krb5_data salt;
324148c5f43SAlan Wright 	krb5_error_code code;
325148c5f43SAlan Wright 	krb5_principal salt_princ;
326faa1795aSjb 	int i, j;
32755bf511dSas 
328148c5f43SAlan Wright 	if (smb_krb5_kt_open(ctx, fname, &kt) != 0)
329148c5f43SAlan Wright 		return (-1);
330148c5f43SAlan Wright 
331148c5f43SAlan Wright 	if (smb_krb5_get_kprinc(ctx, SMB_KRB5_PN_ID_SALT, SMB_PN_SALT,
332148c5f43SAlan Wright 	    fqdn, &salt_princ) != 0) {
333148c5f43SAlan Wright 		(void) krb5_kt_close(ctx, kt);
334148c5f43SAlan Wright 		return (-1);
335148c5f43SAlan Wright 	}
336148c5f43SAlan Wright 
337148c5f43SAlan Wright 	code = krb5_principal2salt(ctx, salt_princ, &salt);
338148c5f43SAlan Wright 	if (code != 0) {
339148c5f43SAlan Wright 		smb_krb5_log_errmsg(ctx, "smbns_ksetpwd: salt computation "
340148c5f43SAlan Wright 		    "failed", code);
341148c5f43SAlan Wright 		krb5_free_principal(ctx, salt_princ);
342148c5f43SAlan Wright 		(void) krb5_kt_close(ctx, kt);
34355bf511dSas 		return (-1);
344148c5f43SAlan Wright 	}
34555bf511dSas 
346148c5f43SAlan Wright 	for (j = 0; j < count; j++) {
347faa1795aSjb 		for (i = 0; i < enctype_count; i++) {
348148c5f43SAlan Wright 			if (smb_krb5_kt_addkey(ctx, kt, princs[j], enctypes[i],
349148c5f43SAlan Wright 			    kvno, &salt, passwd) != 0) {
350148c5f43SAlan Wright 				krb5_free_principal(ctx, salt_princ);
351148c5f43SAlan Wright 				krb5_xfree(salt.data);
3525ad42b1bSSurya Prakki 				(void) krb5_kt_close(ctx, kt);
353faa1795aSjb 				return (-1);
354faa1795aSjb 			}
355faa1795aSjb 		}
356faa1795aSjb 
35755bf511dSas 	}
358148c5f43SAlan Wright 	krb5_free_principal(ctx, salt_princ);
359148c5f43SAlan Wright 	krb5_xfree(salt.data);
3605ad42b1bSSurya Prakki 	(void) krb5_kt_close(ctx, kt);
361faa1795aSjb 	return (0);
362faa1795aSjb }
363da6c28aaSamw 
364faa1795aSjb boolean_t
smb_krb5_kt_find(smb_krb5_pn_id_t id,const char * fqdn,char * fname)365148c5f43SAlan Wright smb_krb5_kt_find(smb_krb5_pn_id_t id, const char *fqdn, char *fname)
366faa1795aSjb {
367faa1795aSjb 	krb5_context ctx;
368faa1795aSjb 	krb5_keytab kt;
369faa1795aSjb 	krb5_keytab_entry entry;
370faa1795aSjb 	krb5_principal princ;
371faa1795aSjb 	char ktname[MAXPATHLEN];
372faa1795aSjb 	boolean_t found = B_FALSE;
373faa1795aSjb 
374148c5f43SAlan Wright 	if (!fqdn || !fname)
375faa1795aSjb 		return (found);
376faa1795aSjb 
377148c5f43SAlan Wright 	if (smb_krb5_ctx_init(&ctx) != 0)
378faa1795aSjb 		return (found);
379faa1795aSjb 
380148c5f43SAlan Wright 	if (smb_krb5_get_kprinc(ctx, id, SMB_PN_KEYTAB_ENTRY, fqdn,
381148c5f43SAlan Wright 	    &princ) != 0) {
382faa1795aSjb 		smb_krb5_ctx_fini(ctx);
383faa1795aSjb 		return (found);
384faa1795aSjb 	}
385faa1795aSjb 
386faa1795aSjb 	(void) snprintf(ktname, MAXPATHLEN, "FILE:%s", fname);
387faa1795aSjb 	if (krb5_kt_resolve(ctx, ktname, &kt) == 0) {
388faa1795aSjb 		if (krb5_kt_get_entry(ctx, kt, princ, 0, 0, &entry) == 0) {
389faa1795aSjb 			found = B_TRUE;
3905ad42b1bSSurya Prakki 			(void) krb5_kt_free_entry(ctx, &entry);
391da6c28aaSamw 		}
392da6c28aaSamw 
3935ad42b1bSSurya Prakki 		(void) krb5_kt_close(ctx, kt);
394da6c28aaSamw 	}
395da6c28aaSamw 
396faa1795aSjb 	krb5_free_principal(ctx, princ);
397faa1795aSjb 	smb_krb5_ctx_fini(ctx);
398faa1795aSjb 	return (found);
399da6c28aaSamw }
400da6c28aaSamw 
401da6c28aaSamw /*
402148c5f43SAlan Wright  * Add a key of the specified encryption type for the specified principal
403148c5f43SAlan Wright  * to the keytab file.
404da6c28aaSamw  * Returns 0 on success. Otherwise, returns -1.
405da6c28aaSamw  */
406da6c28aaSamw static int
smb_krb5_kt_addkey(krb5_context ctx,krb5_keytab kt,const krb5_principal princ,krb5_enctype enctype,krb5_kvno kvno,const krb5_data * salt,const char * pw)407148c5f43SAlan Wright smb_krb5_kt_addkey(krb5_context ctx, krb5_keytab kt, const krb5_principal princ,
408148c5f43SAlan Wright     krb5_enctype enctype, krb5_kvno kvno, const krb5_data *salt,
409148c5f43SAlan Wright     const char *pw)
410da6c28aaSamw {
411da6c28aaSamw 	krb5_keytab_entry *entry;
412148c5f43SAlan Wright 	krb5_data password;
413da6c28aaSamw 	krb5_keyblock key;
414da6c28aaSamw 	krb5_error_code code;
415148c5f43SAlan Wright 	char buf[SMB_KRB5_MAX_BUFLEN], msg[SMB_KRB5_MAX_BUFLEN];
416da6c28aaSamw 	int rc = 0;
417da6c28aaSamw 
418da6c28aaSamw 	if ((code = krb5_enctype_to_string(enctype, buf, sizeof (buf)))) {
419148c5f43SAlan Wright 		(void) snprintf(msg, sizeof (msg), "smbns_ksetpwd: unknown "
420148c5f43SAlan Wright 		    "encryption type (%d)", enctype);
421148c5f43SAlan Wright 		smb_krb5_log_errmsg(ctx, msg, code);
422da6c28aaSamw 		return (-1);
423da6c28aaSamw 	}
424da6c28aaSamw 
425da6c28aaSamw 	if ((entry = (krb5_keytab_entry *) malloc(sizeof (*entry))) == NULL) {
426148c5f43SAlan Wright 		syslog(LOG_ERR, "smbns_ksetpwd: possible transient "
427148c5f43SAlan Wright 		    "memory shortage");
428da6c28aaSamw 		return (-1);
429da6c28aaSamw 	}
430da6c28aaSamw 
431da6c28aaSamw 	(void) memset((char *)entry, 0, sizeof (*entry));
432da6c28aaSamw 
433da6c28aaSamw 	password.length = strlen(pw);
434da6c28aaSamw 	password.data = (char *)pw;
435da6c28aaSamw 
436148c5f43SAlan Wright 	code = krb5_c_string_to_key(ctx, enctype, &password, salt, &key);
437da6c28aaSamw 	if (code != 0) {
438148c5f43SAlan Wright 		(void) snprintf(msg, sizeof (msg), "smbns_ksetpwd: failed to "
439148c5f43SAlan Wright 		    "generate key (%d)", enctype);
440148c5f43SAlan Wright 		smb_krb5_log_errmsg(ctx, msg, code);
441da6c28aaSamw 		free(entry);
442da6c28aaSamw 		return (-1);
443da6c28aaSamw 	}
444da6c28aaSamw 
445da6c28aaSamw 	(void) memcpy(&entry->key, &key, sizeof (krb5_keyblock));
446da6c28aaSamw 	entry->vno = kvno;
447da6c28aaSamw 	entry->principal = princ;
448da6c28aaSamw 
449da6c28aaSamw 	if ((code = krb5_kt_add_entry(ctx, kt, entry)) != 0) {
450148c5f43SAlan Wright 		(void) snprintf(msg, sizeof (msg), "smbns_ksetpwd: failed to "
451148c5f43SAlan Wright 		    "add key (%d)", enctype);
452148c5f43SAlan Wright 		smb_krb5_log_errmsg(ctx, msg, code);
453da6c28aaSamw 		rc = -1;
454da6c28aaSamw 	}
455da6c28aaSamw 
456da6c28aaSamw 	free(entry);
457cbfb650aScp 	if (key.length)
458cbfb650aScp 		krb5_free_keyblock_contents(ctx, &key);
459da6c28aaSamw 	return (rc);
460da6c28aaSamw }
461148c5f43SAlan Wright 
462148c5f43SAlan Wright static int
smb_krb5_spn_count(uint32_t type)463148c5f43SAlan Wright smb_krb5_spn_count(uint32_t type)
464148c5f43SAlan Wright {
465148c5f43SAlan Wright 	int i, cnt;
466148c5f43SAlan Wright 
467148c5f43SAlan Wright 	for (i = 0, cnt = 0; i < SMB_KRB5_SPN_TAB_SZ; i++) {
468148c5f43SAlan Wright 		if (smb_krb5_pn_tab[i].p_flags & type)
469148c5f43SAlan Wright 			cnt++;
470148c5f43SAlan Wright 	}
471148c5f43SAlan Wright 
472148c5f43SAlan Wright 	return (cnt);
473148c5f43SAlan Wright }
474148c5f43SAlan Wright 
475148c5f43SAlan Wright /*
476148c5f43SAlan Wright  * Generate the Kerberos Principal given a principal name format and the
477148c5f43SAlan Wright  * fully qualified domain name. On success, caller must free the allocated
478148c5f43SAlan Wright  * memory by calling krb5_free_principal().
479148c5f43SAlan Wright  */
480148c5f43SAlan Wright static int
smb_krb5_get_kprinc(krb5_context ctx,smb_krb5_pn_id_t id,uint32_t type,const char * fqdn,krb5_principal * princ)481148c5f43SAlan Wright smb_krb5_get_kprinc(krb5_context ctx, smb_krb5_pn_id_t id, uint32_t type,
482148c5f43SAlan Wright     const char *fqdn, krb5_principal *princ)
483148c5f43SAlan Wright {
484148c5f43SAlan Wright 	char *buf;
485148c5f43SAlan Wright 
486148c5f43SAlan Wright 	if ((buf = smb_krb5_get_pn_by_id(id, type, fqdn)) == NULL)
487148c5f43SAlan Wright 		return (-1);
488148c5f43SAlan Wright 
489148c5f43SAlan Wright 	if (krb5_parse_name(ctx, buf, princ) != 0) {
490148c5f43SAlan Wright 		free(buf);
491148c5f43SAlan Wright 		return (-1);
492148c5f43SAlan Wright 	}
493148c5f43SAlan Wright 
494148c5f43SAlan Wright 	free(buf);
495148c5f43SAlan Wright 	return (0);
496148c5f43SAlan Wright }
497148c5f43SAlan Wright 
498148c5f43SAlan Wright /*
499148c5f43SAlan Wright  * Looks up an entry in the principal name table given the ID.
500148c5f43SAlan Wright  */
501148c5f43SAlan Wright static smb_krb5_pn_t *
smb_krb5_lookup_pn(smb_krb5_pn_id_t id)502148c5f43SAlan Wright smb_krb5_lookup_pn(smb_krb5_pn_id_t id)
503148c5f43SAlan Wright {
504148c5f43SAlan Wright 	int i;
505148c5f43SAlan Wright 	smb_krb5_pn_t *tabent;
506148c5f43SAlan Wright 
507148c5f43SAlan Wright 	for (i = 0; i < SMB_KRB5_SPN_TAB_SZ; i++) {
508148c5f43SAlan Wright 		tabent = &smb_krb5_pn_tab[i];
509148c5f43SAlan Wright 		if (id == tabent->p_id)
510148c5f43SAlan Wright 			return (tabent);
511148c5f43SAlan Wright 	}
512148c5f43SAlan Wright 
513148c5f43SAlan Wright 	return (NULL);
514148c5f43SAlan Wright }
515148c5f43SAlan Wright 
516148c5f43SAlan Wright /*
517148c5f43SAlan Wright  * Construct the principal name given an ID, the requested type, and the
518148c5f43SAlan Wright  * fully-qualified name of the domain of which the principal is a member.
519148c5f43SAlan Wright  */
520148c5f43SAlan Wright static char *
smb_krb5_get_pn_by_id(smb_krb5_pn_id_t id,uint32_t type,const char * fqdn)521148c5f43SAlan Wright smb_krb5_get_pn_by_id(smb_krb5_pn_id_t id, uint32_t type,
522148c5f43SAlan Wright     const char *fqdn)
523148c5f43SAlan Wright {
524148c5f43SAlan Wright 	char nbname[NETBIOS_NAME_SZ];
525148c5f43SAlan Wright 	char hostname[MAXHOSTNAMELEN];
526148c5f43SAlan Wright 	char *realm = NULL;
527148c5f43SAlan Wright 	smb_krb5_pn_t *pn;
528148c5f43SAlan Wright 	char *buf;
529148c5f43SAlan Wright 
530148c5f43SAlan Wright 	(void) smb_getnetbiosname(nbname, NETBIOS_NAME_SZ);
531148c5f43SAlan Wright 	(void) smb_gethostname(hostname, MAXHOSTNAMELEN, SMB_CASE_LOWER);
532148c5f43SAlan Wright 
533148c5f43SAlan Wright 	pn = smb_krb5_lookup_pn(id);
534148c5f43SAlan Wright 
535148c5f43SAlan Wright 	/* detect inconsistent requested format and type */
536148c5f43SAlan Wright 	if ((type & pn->p_flags) != type)
537148c5f43SAlan Wright 		return (NULL);
538148c5f43SAlan Wright 
539148c5f43SAlan Wright 	switch (id) {
540148c5f43SAlan Wright 	case SMB_KRB5_PN_ID_SALT:
541148c5f43SAlan Wright 		(void) asprintf(&buf, "%s/%s.%s",
542148c5f43SAlan Wright 		    pn->p_svc, smb_strlwr(nbname), fqdn);
543148c5f43SAlan Wright 		break;
544148c5f43SAlan Wright 
545148c5f43SAlan Wright 	case SMB_KRB5_PN_ID_HOST_FQHN:
54612b65585SGordon Ross 	case SMB_KRB5_PN_ID_CIFS_FQHN:
547148c5f43SAlan Wright 	case SMB_KRB5_PN_ID_NFS_FQHN:
548148c5f43SAlan Wright 	case SMB_KRB5_PN_ID_HTTP_FQHN:
549148c5f43SAlan Wright 	case SMB_KRB5_PN_ID_ROOT_FQHN:
550148c5f43SAlan Wright 		(void) asprintf(&buf, "%s/%s.%s",
551148c5f43SAlan Wright 		    pn->p_svc, hostname, fqdn);
552148c5f43SAlan Wright 		break;
55312b65585SGordon Ross 
55412b65585SGordon Ross 	case SMB_KRB5_PN_ID_HOST_SHORT:
55512b65585SGordon Ross 	case SMB_KRB5_PN_ID_CIFS_SHORT:
55612b65585SGordon Ross 		(void) asprintf(&buf, "%s/%s",
55712b65585SGordon Ross 		    pn->p_svc, nbname);
55812b65585SGordon Ross 		break;
55912b65585SGordon Ross 
56012b65585SGordon Ross 	/*
56112b65585SGordon Ross 	 * SPN for the machine account, which is simply the
56212b65585SGordon Ross 	 * (short) machine name with a dollar sign appended.
56312b65585SGordon Ross 	 */
56412b65585SGordon Ross 	case SMB_KRB5_PN_ID_MACHINE:
56512b65585SGordon Ross 		(void) asprintf(&buf, "%s$", nbname);
56612b65585SGordon Ross 		break;
56712b65585SGordon Ross 
56812b65585SGordon Ross 	default:
56912b65585SGordon Ross 		return (NULL);
570148c5f43SAlan Wright 	}
571148c5f43SAlan Wright 
572148c5f43SAlan Wright 	/*
573148c5f43SAlan Wright 	 * If the requested principal is either added to keytab / the machine
574148c5f43SAlan Wright 	 * account as the UPN attribute or used for key salt generation,
575148c5f43SAlan Wright 	 * the principal name must have the @<REALM> portion.
576148c5f43SAlan Wright 	 */
577148c5f43SAlan Wright 	if (type & (SMB_PN_KEYTAB_ENTRY | SMB_PN_UPN_ATTR | SMB_PN_SALT)) {
578148c5f43SAlan Wright 		if ((realm = strdup(fqdn)) == NULL) {
579148c5f43SAlan Wright 			free(buf);
580148c5f43SAlan Wright 			return (NULL);
581148c5f43SAlan Wright 		}
582148c5f43SAlan Wright 
583148c5f43SAlan Wright 		(void) smb_strupr(realm);
584148c5f43SAlan Wright 		if (buf != NULL) {
585148c5f43SAlan Wright 			char *tmp;
586148c5f43SAlan Wright 
587148c5f43SAlan Wright 			(void) asprintf(&tmp, "%s@%s", buf,
588148c5f43SAlan Wright 			    realm);
589148c5f43SAlan Wright 			free(buf);
590148c5f43SAlan Wright 			buf = tmp;
591148c5f43SAlan Wright 		}
592148c5f43SAlan Wright 
593148c5f43SAlan Wright 		free(realm);
594148c5f43SAlan Wright 	}
595148c5f43SAlan Wright 
596148c5f43SAlan Wright 	return (buf);
597148c5f43SAlan Wright }
598