dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / smbsrv / libsmbns / common / smbns_ksetpwd.c
blob9478b77291fe6f036fcf9f1b1429481f18268675
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <strings.h>
31 #include <unistd.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <syslog.h>
35 #include <netdb.h>
36 #include <sys/param.h>
37 #include <kerberosv5/krb5.h>
38 #include <kerberosv5/com_err.h>
40 #include <smbsrv/libsmb.h>
41 #include <smbns_krb.h>
44 * Kerberized services available on the system.
46 static smb_krb5_pn_t smb_krb5_pn_tab[] = {
48 * Service keys are salted with the SMB_KRB_PN_ID_ID_SALT prinipal
49 * name.
51 {SMB_KRB5_PN_ID_SALT, SMB_PN_SVC_HOST, SMB_PN_SALT},
53 /* CIFS SPNs. (HOST, CIFS, ...) */
54 {SMB_KRB5_PN_ID_HOST_FQHN, SMB_PN_SVC_HOST,
55 SMB_PN_KEYTAB_ENTRY | SMB_PN_SPN_ATTR | SMB_PN_UPN_ATTR},
56 {SMB_KRB5_PN_ID_HOST_SHORT, SMB_PN_SVC_HOST,
57 SMB_PN_KEYTAB_ENTRY | SMB_PN_SPN_ATTR},
58 {SMB_KRB5_PN_ID_CIFS_FQHN, SMB_PN_SVC_CIFS,
59 SMB_PN_KEYTAB_ENTRY | SMB_PN_SPN_ATTR},
60 {SMB_KRB5_PN_ID_CIFS_SHORT, SMB_PN_SVC_CIFS,
61 SMB_PN_KEYTAB_ENTRY | SMB_PN_SPN_ATTR},
62 {SMB_KRB5_PN_ID_MACHINE, NULL,
63 SMB_PN_KEYTAB_ENTRY},
65 /* NFS */
66 {SMB_KRB5_PN_ID_NFS_FQHN, SMB_PN_SVC_NFS,
67 SMB_PN_KEYTAB_ENTRY | SMB_PN_SPN_ATTR},
69 /* HTTP */
70 {SMB_KRB5_PN_ID_HTTP_FQHN, SMB_PN_SVC_HTTP,
71 SMB_PN_KEYTAB_ENTRY | SMB_PN_SPN_ATTR},
73 /* ROOT */
74 {SMB_KRB5_PN_ID_ROOT_FQHN, SMB_PN_SVC_ROOT,
75 SMB_PN_KEYTAB_ENTRY | SMB_PN_SPN_ATTR},
78 #define SMB_KRB5_SPN_TAB_SZ \
79 (sizeof (smb_krb5_pn_tab) / sizeof (smb_krb5_pn_tab[0]))
81 #define SMB_KRB5_MAX_BUFLEN 128
83 static int smb_krb5_kt_open(krb5_context, char *, krb5_keytab *);
84 static int smb_krb5_kt_addkey(krb5_context, krb5_keytab, const krb5_principal,
85 krb5_enctype, krb5_kvno, const krb5_data *, const char *);
86 static int smb_krb5_spn_count(uint32_t);
87 static smb_krb5_pn_t *smb_krb5_lookup_pn(smb_krb5_pn_id_t);
88 static char *smb_krb5_get_pn_by_id(smb_krb5_pn_id_t, uint32_t,
89 const char *);
90 static int smb_krb5_get_kprinc(krb5_context, smb_krb5_pn_id_t, uint32_t,
91 const char *, krb5_principal *);
95 * Generates a null-terminated array of principal names that
96 * represents the list of the available Kerberized services
97 * of the specified type (SPN attribute, UPN attribute, or
98 * keytab entry).
100 * Returns the number of principal names returned via the 1st
101 * output parameter (i.e. vals).
103 * Caller must invoke smb_krb5_free_spns to free the allocated
104 * memory when finished.
106 uint32_t
107 smb_krb5_get_pn_set(smb_krb5_pn_set_t *set, uint32_t type, char *fqdn)
109 int cnt, i;
110 smb_krb5_pn_t *tabent;
112 if (!set || !fqdn)
113 return (0);
115 bzero(set, sizeof (smb_krb5_pn_set_t));
116 cnt = smb_krb5_spn_count(type);
117 set->s_pns = (char **)calloc(cnt + 1, sizeof (char *));
119 if (set->s_pns == NULL)
120 return (0);
122 for (i = 0, set->s_cnt = 0; i < SMB_KRB5_SPN_TAB_SZ; i++) {
123 tabent = &smb_krb5_pn_tab[i];
125 if (set->s_cnt == cnt)
126 break;
128 if ((tabent->p_flags & type) != type)
129 continue;
131 set->s_pns[set->s_cnt] = smb_krb5_get_pn_by_id(tabent->p_id,
132 type, fqdn);
133 if (set->s_pns[set->s_cnt] == NULL) {
134 syslog(LOG_ERR, "smbns_ksetpwd: failed to obtain "
135 "principal names: possible transient memory "
136 "shortage");
137 smb_krb5_free_pn_set(set);
138 return (0);
141 set->s_cnt++;
144 if (set->s_cnt == 0)
145 smb_krb5_free_pn_set(set);
147 return (set->s_cnt);
150 void
151 smb_krb5_free_pn_set(smb_krb5_pn_set_t *set)
153 int i;
155 if (set == NULL || set->s_pns == NULL)
156 return;
158 for (i = 0; i < set->s_cnt; i++)
159 free(set->s_pns[i]);
161 free(set->s_pns);
162 set->s_pns = NULL;
166 * Initialize the kerberos context.
167 * Return 0 on success. Otherwise, return -1.
170 smb_krb5_ctx_init(krb5_context *ctx)
172 if (krb5_init_context(ctx) != 0)
173 return (-1);
175 return (0);
179 * Free the kerberos context.
181 void
182 smb_krb5_ctx_fini(krb5_context ctx)
184 krb5_free_context(ctx);
188 * Create an array of Kerberos Princiapls given an array of principal names.
189 * Caller must free the allocated memory using smb_krb5_free_kprincs()
190 * upon success.
192 * Returns 0 on success. Otherwise, returns -1.
195 smb_krb5_get_kprincs(krb5_context ctx, char **names, size_t num,
196 krb5_principal **krb5princs)
198 int i;
200 if ((*krb5princs = calloc(num, sizeof (krb5_principal *))) == NULL) {
201 return (-1);
204 for (i = 0; i < num; i++) {
205 if (krb5_parse_name(ctx, names[i], &(*krb5princs)[i]) != 0) {
206 smb_krb5_free_kprincs(ctx, *krb5princs, i);
207 return (-1);
211 return (0);
214 void
215 smb_krb5_free_kprincs(krb5_context ctx, krb5_principal *krb5princs,
216 size_t num)
218 int i;
220 for (i = 0; i < num; i++)
221 krb5_free_principal(ctx, krb5princs[i]);
223 free(krb5princs);
227 * Set the workstation trust account password.
228 * Returns 0 on success. Otherwise, returns non-zero value.
231 smb_krb5_setpwd(krb5_context ctx, const char *fqdn, char *passwd)
233 krb5_error_code code;
234 krb5_ccache cc = NULL;
235 int result_code = 0;
236 krb5_data result_code_string, result_string;
237 krb5_principal princ;
238 char msg[SMB_KRB5_MAX_BUFLEN];
240 if (smb_krb5_get_kprinc(ctx, SMB_KRB5_PN_ID_HOST_FQHN,
241 SMB_PN_UPN_ATTR, fqdn, &princ) != 0)
242 return (-1);
244 (void) memset(&result_code_string, 0, sizeof (result_code_string));
245 (void) memset(&result_string, 0, sizeof (result_string));
247 if ((code = krb5_cc_default(ctx, &cc)) != 0) {
248 (void) snprintf(msg, sizeof (msg), "smbns_ksetpwd: failed to "
249 "find %s", SMB_CCACHE_PATH);
250 smb_krb5_log_errmsg(ctx, msg, code);
251 krb5_free_principal(ctx, princ);
252 return (-1);
255 code = krb5_set_password_using_ccache(ctx, cc, passwd, princ,
256 &result_code, &result_code_string, &result_string);
258 (void) krb5_cc_close(ctx, cc);
260 if (code != 0)
261 smb_krb5_log_errmsg(ctx, "smbns_ksetpwd: KPASSWD protocol "
262 "exchange failed", code);
264 if (result_code != 0) {
265 syslog(LOG_ERR, "smbns_ksetpwd: KPASSWD failed: rc=%d %.*s",
266 result_code,
267 result_code_string.length,
268 result_code_string.data);
269 if (code == 0)
270 code = EACCES;
273 krb5_free_principal(ctx, princ);
274 free(result_code_string.data);
275 free(result_string.data);
276 return (code);
280 * Open the keytab file for writing.
281 * The keytab should be closed by calling krb5_kt_close().
283 static int
284 smb_krb5_kt_open(krb5_context ctx, char *fname, krb5_keytab *kt)
286 char *ktname;
287 krb5_error_code code;
288 int len;
289 char msg[SMB_KRB5_MAX_BUFLEN];
291 *kt = NULL;
292 len = snprintf(NULL, 0, "WRFILE:%s", fname) + 1;
293 if ((ktname = malloc(len)) == NULL) {
294 syslog(LOG_ERR, "smbns_ksetpwd: unable to open keytab %s: "
295 "possible transient memory shortage", fname);
296 return (-1);
299 (void) snprintf(ktname, len, "WRFILE:%s", fname);
301 if ((code = krb5_kt_resolve(ctx, ktname, kt)) != 0) {
302 (void) snprintf(msg, sizeof (msg), "smbns_ksetpwd: %s", fname);
303 smb_krb5_log_errmsg(ctx, msg, code);
304 free(ktname);
305 return (-1);
308 free(ktname);
309 return (0);
313 * Populate the keytab with keys of the specified key version for the
314 * specified set of krb5 principals. All service keys will be salted by:
315 * host/<truncated@15_lower_case_hostname>.<fqdn>@<REALM>
318 smb_krb5_kt_populate(krb5_context ctx, const char *fqdn,
319 krb5_principal *princs, int count, char *fname, krb5_kvno kvno,
320 char *passwd, krb5_enctype *enctypes, int enctype_count)
322 krb5_keytab kt = NULL;
323 krb5_data salt;
324 krb5_error_code code;
325 krb5_principal salt_princ;
326 int i, j;
328 if (smb_krb5_kt_open(ctx, fname, &kt) != 0)
329 return (-1);
331 if (smb_krb5_get_kprinc(ctx, SMB_KRB5_PN_ID_SALT, SMB_PN_SALT,
332 fqdn, &salt_princ) != 0) {
333 (void) krb5_kt_close(ctx, kt);
334 return (-1);
337 code = krb5_principal2salt(ctx, salt_princ, &salt);
338 if (code != 0) {
339 smb_krb5_log_errmsg(ctx, "smbns_ksetpwd: salt computation "
340 "failed", code);
341 krb5_free_principal(ctx, salt_princ);
342 (void) krb5_kt_close(ctx, kt);
343 return (-1);
346 for (j = 0; j < count; j++) {
347 for (i = 0; i < enctype_count; i++) {
348 if (smb_krb5_kt_addkey(ctx, kt, princs[j], enctypes[i],
349 kvno, &salt, passwd) != 0) {
350 krb5_free_principal(ctx, salt_princ);
351 krb5_xfree(salt.data);
352 (void) krb5_kt_close(ctx, kt);
353 return (-1);
358 krb5_free_principal(ctx, salt_princ);
359 krb5_xfree(salt.data);
360 (void) krb5_kt_close(ctx, kt);
361 return (0);
364 boolean_t
365 smb_krb5_kt_find(smb_krb5_pn_id_t id, const char *fqdn, char *fname)
367 krb5_context ctx;
368 krb5_keytab kt;
369 krb5_keytab_entry entry;
370 krb5_principal princ;
371 char ktname[MAXPATHLEN];
372 boolean_t found = B_FALSE;
374 if (!fqdn || !fname)
375 return (found);
377 if (smb_krb5_ctx_init(&ctx) != 0)
378 return (found);
380 if (smb_krb5_get_kprinc(ctx, id, SMB_PN_KEYTAB_ENTRY, fqdn,
381 &princ) != 0) {
382 smb_krb5_ctx_fini(ctx);
383 return (found);
386 (void) snprintf(ktname, MAXPATHLEN, "FILE:%s", fname);
387 if (krb5_kt_resolve(ctx, ktname, &kt) == 0) {
388 if (krb5_kt_get_entry(ctx, kt, princ, 0, 0, &entry) == 0) {
389 found = B_TRUE;
390 (void) krb5_kt_free_entry(ctx, &entry);
393 (void) krb5_kt_close(ctx, kt);
396 krb5_free_principal(ctx, princ);
397 smb_krb5_ctx_fini(ctx);
398 return (found);
402 * Add a key of the specified encryption type for the specified principal
403 * to the keytab file.
404 * Returns 0 on success. Otherwise, returns -1.
406 static int
407 smb_krb5_kt_addkey(krb5_context ctx, krb5_keytab kt, const krb5_principal princ,
408 krb5_enctype enctype, krb5_kvno kvno, const krb5_data *salt,
409 const char *pw)
411 krb5_keytab_entry *entry;
412 krb5_data password;
413 krb5_keyblock key;
414 krb5_error_code code;
415 char buf[SMB_KRB5_MAX_BUFLEN], msg[SMB_KRB5_MAX_BUFLEN];
416 int rc = 0;
418 if ((code = krb5_enctype_to_string(enctype, buf, sizeof (buf)))) {
419 (void) snprintf(msg, sizeof (msg), "smbns_ksetpwd: unknown "
420 "encryption type (%d)", enctype);
421 smb_krb5_log_errmsg(ctx, msg, code);
422 return (-1);
425 if ((entry = (krb5_keytab_entry *) malloc(sizeof (*entry))) == NULL) {
426 syslog(LOG_ERR, "smbns_ksetpwd: possible transient "
427 "memory shortage");
428 return (-1);
431 (void) memset((char *)entry, 0, sizeof (*entry));
433 password.length = strlen(pw);
434 password.data = (char *)pw;
436 code = krb5_c_string_to_key(ctx, enctype, &password, salt, &key);
437 if (code != 0) {
438 (void) snprintf(msg, sizeof (msg), "smbns_ksetpwd: failed to "
439 "generate key (%d)", enctype);
440 smb_krb5_log_errmsg(ctx, msg, code);
441 free(entry);
442 return (-1);
445 (void) memcpy(&entry->key, &key, sizeof (krb5_keyblock));
446 entry->vno = kvno;
447 entry->principal = princ;
449 if ((code = krb5_kt_add_entry(ctx, kt, entry)) != 0) {
450 (void) snprintf(msg, sizeof (msg), "smbns_ksetpwd: failed to "
451 "add key (%d)", enctype);
452 smb_krb5_log_errmsg(ctx, msg, code);
453 rc = -1;
456 free(entry);
457 if (key.length)
458 krb5_free_keyblock_contents(ctx, &key);
459 return (rc);
462 static int
463 smb_krb5_spn_count(uint32_t type)
465 int i, cnt;
467 for (i = 0, cnt = 0; i < SMB_KRB5_SPN_TAB_SZ; i++) {
468 if (smb_krb5_pn_tab[i].p_flags & type)
469 cnt++;
472 return (cnt);
476 * Generate the Kerberos Principal given a principal name format and the
477 * fully qualified domain name. On success, caller must free the allocated
478 * memory by calling krb5_free_principal().
480 static int
481 smb_krb5_get_kprinc(krb5_context ctx, smb_krb5_pn_id_t id, uint32_t type,
482 const char *fqdn, krb5_principal *princ)
484 char *buf;
486 if ((buf = smb_krb5_get_pn_by_id(id, type, fqdn)) == NULL)
487 return (-1);
489 if (krb5_parse_name(ctx, buf, princ) != 0) {
490 free(buf);
491 return (-1);
494 free(buf);
495 return (0);
499 * Looks up an entry in the principal name table given the ID.
501 static smb_krb5_pn_t *
502 smb_krb5_lookup_pn(smb_krb5_pn_id_t id)
504 int i;
505 smb_krb5_pn_t *tabent;
507 for (i = 0; i < SMB_KRB5_SPN_TAB_SZ; i++) {
508 tabent = &smb_krb5_pn_tab[i];
509 if (id == tabent->p_id)
510 return (tabent);
513 return (NULL);
517 * Construct the principal name given an ID, the requested type, and the
518 * fully-qualified name of the domain of which the principal is a member.
520 static char *
521 smb_krb5_get_pn_by_id(smb_krb5_pn_id_t id, uint32_t type,
522 const char *fqdn)
524 char nbname[NETBIOS_NAME_SZ];
525 char hostname[MAXHOSTNAMELEN];
526 char *realm = NULL;
527 smb_krb5_pn_t *pn;
528 char *buf;
530 (void) smb_getnetbiosname(nbname, NETBIOS_NAME_SZ);
531 (void) smb_gethostname(hostname, MAXHOSTNAMELEN, SMB_CASE_LOWER);
533 pn = smb_krb5_lookup_pn(id);
535 /* detect inconsistent requested format and type */
536 if ((type & pn->p_flags) != type)
537 return (NULL);
539 switch (id) {
540 case SMB_KRB5_PN_ID_SALT:
541 (void) asprintf(&buf, "%s/%s.%s",
542 pn->p_svc, smb_strlwr(nbname), fqdn);
543 break;
545 case SMB_KRB5_PN_ID_HOST_FQHN:
546 case SMB_KRB5_PN_ID_CIFS_FQHN:
547 case SMB_KRB5_PN_ID_NFS_FQHN:
548 case SMB_KRB5_PN_ID_HTTP_FQHN:
549 case SMB_KRB5_PN_ID_ROOT_FQHN:
550 (void) asprintf(&buf, "%s/%s.%s",
551 pn->p_svc, hostname, fqdn);
552 break;
554 case SMB_KRB5_PN_ID_HOST_SHORT:
555 case SMB_KRB5_PN_ID_CIFS_SHORT:
556 (void) asprintf(&buf, "%s/%s",
557 pn->p_svc, nbname);
558 break;
561 * SPN for the machine account, which is simply the
562 * (short) machine name with a dollar sign appended.
564 case SMB_KRB5_PN_ID_MACHINE:
565 (void) asprintf(&buf, "%s$", nbname);
566 break;
568 default:
569 return (NULL);
573 * If the requested principal is either added to keytab / the machine
574 * account as the UPN attribute or used for key salt generation,
575 * the principal name must have the @<REALM> portion.
577 if (type & (SMB_PN_KEYTAB_ENTRY | SMB_PN_UPN_ATTR | SMB_PN_SALT)) {
578 if ((realm = strdup(fqdn)) == NULL) {
579 free(buf);
580 return (NULL);
583 (void) smb_strupr(realm);
584 if (buf != NULL) {
585 char *tmp;
587 (void) asprintf(&tmp, "%s@%s", buf,
588 realm);
589 free(buf);
590 buf = tmp;
593 free(realm);
596 return (buf);