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