xref: /illumos-gate/usr/src/lib/libsmbfs/smb/keychain.c (revision 9ee48d48fcedfa1c02bcd16d6abbbfd28b9726c1)
1 /*
2  * CDDL HEADER START
3  *
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.
7  *
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.
12  *
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]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
24  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 /*
29  * External interface to the libsmbfs/netsmb keychain
30  * storage mechanism.  This interface is consumed by
31  * the "smbutil" commands: login, logout, ...
32  * and by the SMBFS PAM module.
33  */
34 
35 #include <sys/types.h>
36 
37 #include <errno.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <libintl.h>
43 
44 #include <cflib.h>
45 #include <netsmb/smb_dev.h>
46 #include <netsmb/smb_lib.h>
47 #include <netsmb/smb_keychain.h>
48 
49 #include "charsets.h"
50 #include "private.h"
51 #include "ntlm.h"
52 
53 /* common func. for add/del/chk */
54 static int
55 smbfs_keychain_cmn(
56 	int cmd,
57 	uid_t uid,
58 	const char *dom,
59 	const char *usr,
60 	uchar_t *lmhash,
61 	uchar_t *nthash)
62 {
63 	smbioc_pk_t pk;
64 	int err, fd, sz;
65 
66 	memset(&pk, 0, sizeof (pk));
67 	pk.pk_uid = uid;
68 	err = 0;
69 	fd = -1;
70 
71 	switch (cmd) {
72 
73 	case SMBIOC_PK_ADD:
74 		/*
75 		 * Add password hashes to the keychain.
76 		 */
77 		if (lmhash == NULL || nthash == NULL) {
78 			err = SMB_KEYCHAIN_BADPASSWD;
79 			goto out;
80 		}
81 		memcpy(pk.pk_lmhash, lmhash, SMBIOC_HASH_SZ);
82 		memcpy(pk.pk_nthash, nthash, SMBIOC_HASH_SZ);
83 		/* FALLTHROUGH */
84 
85 	case SMBIOC_PK_CHK:
86 	case SMBIOC_PK_DEL:
87 		/*
88 		 * Copy domain and user.
89 		 */
90 		if (dom == NULL) {
91 			err = SMB_KEYCHAIN_BADDOMAIN;
92 			goto out;
93 		}
94 		sz = sizeof (pk.pk_dom);
95 		if (strlcpy(pk.pk_dom, dom, sz) >= sz) {
96 			err = SMB_KEYCHAIN_BADDOMAIN;
97 			goto out;
98 		}
99 		if (usr == NULL) {
100 			err = SMB_KEYCHAIN_BADUSER;
101 			goto out;
102 		}
103 		sz = sizeof (pk.pk_usr);
104 		if (strlcpy(pk.pk_usr, usr, sz) >= sz) {
105 			err = SMB_KEYCHAIN_BADUSER;
106 			goto out;
107 		}
108 		break;
109 
110 	case SMBIOC_PK_DEL_OWNER:	/* all owned by the caller */
111 	case SMBIOC_PK_DEL_EVERYONE:	/* all owned by everyone */
112 		/*
113 		 * These two do not copyin any args, but we'll
114 		 * pass pk here anyway just so we can use the
115 		 * common code path below.
116 		 */
117 		break;
118 
119 	default:
120 		err = SMB_KEYCHAIN_UNKNOWN;
121 		goto out;
122 	}
123 
124 	fd = smb_open_driver();
125 	if (fd < 0) {
126 		err = SMB_KEYCHAIN_NODRIVER;
127 		goto out;
128 	}
129 
130 	err = 0;
131 	if (ioctl(fd, cmd, &pk) < 0) {
132 		err = errno;
133 		goto out;
134 	}
135 
136 	if (cmd == SMBIOC_PK_CHK) {
137 		if (lmhash != NULL)
138 			memcpy(lmhash, pk.pk_lmhash, SMBIOC_HASH_SZ);
139 		if (nthash != NULL)
140 			memcpy(nthash, pk.pk_nthash, SMBIOC_HASH_SZ);
141 	}
142 
143 out:
144 	if (fd != -1)
145 		close(fd);
146 
147 	return (err);
148 }
149 
150 /*
151  * Add a password to the keychain.
152  *
153  * Note: pass is a cleartext password.
154  * We use it here to compute the LM hash and NT hash,
155  * and then store ONLY the hashes.
156  */
157 int
158 smbfs_keychain_add(uid_t uid, const char *dom, const char *usr,
159 	const char *pass)
160 {
161 	uchar_t lmhash[SMBIOC_HASH_SZ];
162 	uchar_t nthash[SMBIOC_HASH_SZ];
163 	int err, cmd = SMBIOC_PK_ADD;
164 
165 	if (pass == NULL)
166 		return (SMB_KEYCHAIN_BADPASSWD);
167 
168 	if ((err = ntlm_compute_lm_hash(lmhash, pass)) != 0)
169 		return (err);
170 	if ((err = ntlm_compute_nt_hash(nthash, pass)) != 0)
171 		return (err);
172 
173 	err = smbfs_keychain_cmn(cmd, uid, dom, usr, lmhash, nthash);
174 	return (err);
175 }
176 
177 /* Variant of the above that takes an NT hash. */
178 int
179 smbfs_keychain_addhash(uid_t uid, const char *dom, const char *usr,
180 	const uchar_t *nthash)
181 {
182 	static const uchar_t lmhash[SMBIOC_HASH_SZ] = { 0 };
183 	int err, cmd = SMBIOC_PK_ADD;
184 	err = smbfs_keychain_cmn(cmd, uid, dom, usr,
185 	    (uchar_t *)lmhash, (uchar_t *)nthash);
186 	return (err);
187 }
188 
189 /* Delete a password from the keychain. */
190 int
191 smbfs_keychain_del(uid_t uid, const char *dom, const char *usr)
192 {
193 	return (smbfs_keychain_cmn(SMBIOC_PK_DEL, uid, dom, usr, NULL, NULL));
194 }
195 
196 /*
197  * Check for existence of a keychain entry.
198  * Returns 0 if it exists, else ENOENT.
199  */
200 int
201 smbfs_keychain_chk(const char *dom, const char *usr)
202 {
203 	uid_t uid = (uid_t)-1;
204 	return (smbfs_keychain_cmn(SMBIOC_PK_CHK, uid, dom, usr, NULL, NULL));
205 }
206 
207 /*
208  * Get the stored hashes
209  */
210 int
211 smbfs_keychain_get(const char *dom, const char *usr,
212 		uchar_t *lmhash, uchar_t *nthash)
213 {
214 	uid_t uid = (uid_t)-1;
215 	int err, cmd = SMBIOC_PK_CHK;
216 
217 	err = smbfs_keychain_cmn(cmd, uid, dom, usr, lmhash, nthash);
218 	return (err);
219 }
220 
221 /*
222  * Delete all keychain entries owned by the caller.
223  */
224 int
225 smbfs_keychain_del_owner()
226 {
227 	int cmd = SMBIOC_PK_DEL_OWNER;
228 	uid_t uid = getuid();
229 	return (smbfs_keychain_cmn(cmd, uid, NULL, NULL, NULL, NULL));
230 }
231 
232 /*
233  * Delete all keychain entries (regardless of onwer).
234  * Requires super-user privliege.
235  */
236 int
237 smbfs_keychain_del_everyone()
238 {
239 	int cmd = SMBIOC_PK_DEL_EVERYONE;
240 	uid_t uid = getuid();
241 	return (smbfs_keychain_cmn(cmd, uid, NULL, NULL, NULL, NULL));
242 }
243 
244 /*
245  * Private function to get keychain p/w hashes.
246  */
247 int
248 smb_get_keychain(struct smb_ctx *ctx)
249 {
250 	int err;
251 
252 	if (ctx->ct_fullserver == NULL) {
253 		DPRINT("ct_fullserver == NULL");
254 		return (EINVAL);
255 	}
256 
257 	/*
258 	 * 1st: try lookup using system name
259 	 */
260 	err = smbfs_keychain_get(ctx->ct_fullserver, ctx->ct_user,
261 	    ctx->ct_lmhash, ctx->ct_nthash);
262 	if (!err) {
263 		ctx->ct_flags |= SMBCF_KCFOUND;
264 		DPRINT("found keychain entry for"
265 		    " server/user: %s/%s\n",
266 		    ctx->ct_fullserver, ctx->ct_user);
267 		return (0);
268 	}
269 
270 	/*
271 	 * 2nd: try lookup using domain name
272 	 */
273 	err = smbfs_keychain_get(ctx->ct_domain, ctx->ct_user,
274 	    ctx->ct_lmhash, ctx->ct_nthash);
275 	if (!err) {
276 		ctx->ct_flags |= (SMBCF_KCFOUND | SMBCF_KCDOMAIN);
277 		DPRINT("found keychain entry for"
278 		    " domain/user: %s/%s\n",
279 		    ctx->ct_domain, ctx->ct_user);
280 		return (0);
281 	}
282 
283 	return (err);
284 }
285 
286 
287 /*
288  * This is not really part of the keychain library,
289  * but is typically needed in code that wants to
290  * provide (editable) defaults for domain/user
291  *
292  * Get default domain and user names
293  * Server name is optional.
294  */
295 int
296 smbfs_default_dom_usr(const char *home, const char *server,
297 	char *dom, int maxdom, char *usr, int maxusr)
298 {
299 	struct smb_ctx  *ctx;
300 	int err;
301 
302 	err = smb_ctx_alloc(&ctx);
303 	if (err)
304 		return (err);
305 
306 	if (server) {
307 		err = smb_ctx_setfullserver(ctx, server);
308 		if (err != 0)
309 			goto out;
310 	}
311 
312 	if (home && *home) {
313 		if (ctx->ct_home)
314 			free(ctx->ct_home);
315 		ctx->ct_home = strdup(home);
316 	}
317 
318 	err = smb_ctx_readrc(ctx);
319 	if (err)
320 		goto out;
321 
322 	if (dom)
323 		strlcpy(dom, ctx->ct_domain, maxdom);
324 
325 	if (usr)
326 		strlcpy(usr, ctx->ct_user, maxusr);
327 
328 out:
329 	smb_ctx_free(ctx);
330 	return (err);
331 }
332