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>
35faa1795aSjb150015 #include <netdb.h>
36faa1795aSjb150015 #include <sys/param.h>
37da6c28aaSamw #include <kerberosv5/krb5.h>
3855bf511dSas200622 #include <kerberosv5/com_err.h>
39faa1795aSjb150015
40faa1795aSjb150015 #include <smbsrv/libsmb.h>
4155bf511dSas200622 #include <smbns_krb.h>
42da6c28aaSamw
43faa1795aSjb150015 /*
44148c5f43SAlan Wright * Kerberized services available on the system.
45faa1795aSjb150015 */
46148c5f43SAlan Wright static smb_krb5_pn_t smb_krb5_pn_tab[] = {
47faa1795aSjb150015 /*
48148c5f43SAlan Wright * Service keys are salted with the SMB_KRB_PN_ID_ID_SALT prinipal
49148c5f43SAlan Wright * name.
50faa1795aSjb150015 */
51148c5f43SAlan Wright {SMB_KRB5_PN_ID_SALT, SMB_PN_SVC_HOST, SMB_PN_SALT},
52faa1795aSjb150015
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},
64faa1795aSjb150015
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},
68faa1795aSjb150015
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},
72faa1795aSjb150015
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 };
77faa1795aSjb150015
78148c5f43SAlan Wright #define SMB_KRB5_SPN_TAB_SZ \
79148c5f43SAlan Wright (sizeof (smb_krb5_pn_tab) / sizeof (smb_krb5_pn_tab[0]))
80faa1795aSjb150015
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 *);
92148c5f43SAlan Wright
93faa1795aSjb150015
94faa1795aSjb150015 /*
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).
99faa1795aSjb150015 *
100148c5f43SAlan Wright * Returns the number of principal names returned via the 1st
101148c5f43SAlan Wright * output parameter (i.e. vals).
102148c5f43SAlan Wright *
103148c5f43SAlan Wright * Caller must invoke smb_krb5_free_spns to free the allocated
104148c5f43SAlan Wright * memory when finished.
105faa1795aSjb150015 */
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)
108faa1795aSjb150015 {
109148c5f43SAlan Wright int cnt, i;
110148c5f43SAlan Wright smb_krb5_pn_t *tabent;
111faa1795aSjb150015
112148c5f43SAlan Wright if (!set || !fqdn)
113148c5f43SAlan Wright return (0);
114faa1795aSjb150015
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 *));
118faa1795aSjb150015
119148c5f43SAlan Wright if (set->s_pns == NULL)
120148c5f43SAlan Wright return (0);
121faa1795aSjb150015
122148c5f43SAlan Wright for (i = 0, set->s_cnt = 0; i < SMB_KRB5_SPN_TAB_SZ; i++) {
123148c5f43SAlan Wright tabent = &smb_krb5_pn_tab[i];
124148c5f43SAlan Wright
125148c5f43SAlan Wright if (set->s_cnt == cnt)
126148c5f43SAlan Wright break;
127148c5f43SAlan Wright
128148c5f43SAlan Wright if ((tabent->p_flags & type) != type)
129148c5f43SAlan Wright continue;
130148c5f43SAlan Wright
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);
139faa1795aSjb150015 }
140faa1795aSjb150015
141148c5f43SAlan Wright set->s_cnt++;
142148c5f43SAlan Wright }
143faa1795aSjb150015
144148c5f43SAlan Wright if (set->s_cnt == 0)
145148c5f43SAlan Wright smb_krb5_free_pn_set(set);
146148c5f43SAlan Wright
147148c5f43SAlan Wright return (set->s_cnt);
148148c5f43SAlan Wright }
149148c5f43SAlan Wright
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;
154148c5f43SAlan Wright
155148c5f43SAlan Wright if (set == NULL || set->s_pns == NULL)
156148c5f43SAlan Wright return;
157148c5f43SAlan Wright
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;
163faa1795aSjb150015 }
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 /*
179da6c28aaSamw * Free the kerberos context.
180da6c28aaSamw */
181da6c28aaSamw void
smb_krb5_ctx_fini(krb5_context ctx)182da6c28aaSamw smb_krb5_ctx_fini(krb5_context ctx)
183da6c28aaSamw {
184da6c28aaSamw krb5_free_context(ctx);
185da6c28aaSamw }
186da6c28aaSamw
187da6c28aaSamw /*
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.
193148c5f43SAlan Wright */
194148c5f43SAlan Wright 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)
197148c5f43SAlan Wright {
198148c5f43SAlan Wright int i;
199148c5f43SAlan Wright
200148c5f43SAlan Wright if ((*krb5princs = calloc(num, sizeof (krb5_principal *))) == NULL) {
201148c5f43SAlan Wright return (-1);
202148c5f43SAlan Wright }
203148c5f43SAlan Wright
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);
207148c5f43SAlan Wright return (-1);
208148c5f43SAlan Wright }
209148c5f43SAlan Wright }
210148c5f43SAlan Wright
211148c5f43SAlan Wright return (0);
212148c5f43SAlan Wright }
213148c5f43SAlan Wright
214148c5f43SAlan Wright 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,
216148c5f43SAlan Wright size_t num)
217148c5f43SAlan Wright {
218148c5f43SAlan Wright int i;
219148c5f43SAlan Wright
220148c5f43SAlan Wright for (i = 0; i < num; i++)
221148c5f43SAlan Wright krb5_free_principal(ctx, krb5princs[i]);
222148c5f43SAlan Wright
223148c5f43SAlan Wright free(krb5princs);
224148c5f43SAlan Wright }
225148c5f43SAlan Wright
226148c5f43SAlan Wright /*
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 }
272cbfb650aScp160787
273148c5f43SAlan Wright krb5_free_principal(ctx, princ);
274cbfb650aScp160787 free(result_code_string.data);
275cbfb650aScp160787 free(result_string.data);
276da6c28aaSamw return (code);
277da6c28aaSamw }
278da6c28aaSamw
279da6c28aaSamw /*
28055bf511dSas200622 * Open the keytab file for writing.
28155bf511dSas200622 * The keytab should be closed by calling krb5_kt_close().
282da6c28aaSamw */
28355bf511dSas200622 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;
28855bf511dSas200622 int len;
289148c5f43SAlan Wright char msg[SMB_KRB5_MAX_BUFLEN];
290da6c28aaSamw
29155bf511dSas200622 *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);
30955bf511dSas200622 return (0);
31055bf511dSas200622 }
31155bf511dSas200622
31255bf511dSas200622 /*
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>
31655bf511dSas200622 */
31755bf511dSas200622 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)
32155bf511dSas200622 {
32255bf511dSas200622 krb5_keytab kt = NULL;
323148c5f43SAlan Wright krb5_data salt;
324148c5f43SAlan Wright krb5_error_code code;
325148c5f43SAlan Wright krb5_principal salt_princ;
326faa1795aSjb150015 int i, j;
32755bf511dSas200622
328148c5f43SAlan Wright if (smb_krb5_kt_open(ctx, fname, &kt) != 0)
32955bf511dSas200622 return (-1);
33055bf511dSas200622
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);
343148c5f43SAlan Wright return (-1);
344148c5f43SAlan Wright }
345148c5f43SAlan Wright
346148c5f43SAlan Wright for (j = 0; j < count; j++) {
347da6c28aaSamw 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);
353faa1795aSjb150015 return (-1);
354faa1795aSjb150015 }
355da6c28aaSamw }
356da6c28aaSamw
357da6c28aaSamw }
358148c5f43SAlan Wright krb5_free_principal(ctx, salt_princ);
359148c5f43SAlan Wright krb5_xfree(salt.data);
3605ad42b1bSSurya Prakki (void) krb5_kt_close(ctx, kt);
361faa1795aSjb150015 return (0);
362faa1795aSjb150015 }
363faa1795aSjb150015
364faa1795aSjb150015 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)
366faa1795aSjb150015 {
367faa1795aSjb150015 krb5_context ctx;
368faa1795aSjb150015 krb5_keytab kt;
369faa1795aSjb150015 krb5_keytab_entry entry;
370faa1795aSjb150015 krb5_principal princ;
371faa1795aSjb150015 char ktname[MAXPATHLEN];
372faa1795aSjb150015 boolean_t found = B_FALSE;
373faa1795aSjb150015
374148c5f43SAlan Wright if (!fqdn || !fname)
375faa1795aSjb150015 return (found);
376faa1795aSjb150015
377148c5f43SAlan Wright if (smb_krb5_ctx_init(&ctx) != 0)
378faa1795aSjb150015 return (found);
379faa1795aSjb150015
380148c5f43SAlan Wright if (smb_krb5_get_kprinc(ctx, id, SMB_PN_KEYTAB_ENTRY, fqdn,
381148c5f43SAlan Wright &princ) != 0) {
382faa1795aSjb150015 smb_krb5_ctx_fini(ctx);
383faa1795aSjb150015 return (found);
384faa1795aSjb150015 }
385faa1795aSjb150015
386faa1795aSjb150015 (void) snprintf(ktname, MAXPATHLEN, "FILE:%s", fname);
387faa1795aSjb150015 if (krb5_kt_resolve(ctx, ktname, &kt) == 0) {
388faa1795aSjb150015 if (krb5_kt_get_entry(ctx, kt, princ, 0, 0, &entry) == 0) {
389faa1795aSjb150015 found = B_TRUE;
3905ad42b1bSSurya Prakki (void) krb5_kt_free_entry(ctx, &entry);
391faa1795aSjb150015 }
392da6c28aaSamw
3935ad42b1bSSurya Prakki (void) krb5_kt_close(ctx, kt);
394faa1795aSjb150015 }
395faa1795aSjb150015
396faa1795aSjb150015 krb5_free_principal(ctx, princ);
397faa1795aSjb150015 smb_krb5_ctx_fini(ctx);
398faa1795aSjb150015 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);
457cbfb650aScp160787 if (key.length)
458cbfb650aScp160787 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