8592 ZFS channel programs - rollback
[unleashed.git] / usr / src / uts / common / fs / smbclnt / netsmb / smb_pass.c
blobc26d80e48c083945bdff39ee66bc304434bd1a2b
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 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * Password Keychain storage mechanism.
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/errno.h>
34 #include <sys/sysmacros.h>
35 #include <sys/uio.h>
36 #include <sys/buf.h>
37 #include <sys/modctl.h>
38 #include <sys/open.h>
39 #include <sys/file.h>
40 #include <sys/kmem.h>
41 #include <sys/conf.h>
42 #include <sys/cmn_err.h>
43 #include <sys/stat.h>
44 #include <sys/ddi.h>
45 #include <sys/sunddi.h>
46 #include <sys/sunldi.h>
47 #include <sys/policy.h>
48 #include <sys/zone.h>
49 #include <sys/pathname.h>
50 #include <sys/mount.h>
51 #include <sys/sdt.h>
52 #include <fs/fs_subr.h>
53 #include <sys/devops.h>
54 #include <sys/thread.h>
55 #include <sys/mkdev.h>
56 #include <sys/avl.h>
57 #include <sys/avl_impl.h>
58 #include <sys/u8_textprep.h>
60 #include <netsmb/smb_osdep.h>
62 #include <netsmb/smb.h>
63 #include <netsmb/smb_conn.h>
64 #include <netsmb/smb_subr.h>
65 #include <netsmb/smb_dev.h>
66 #include <netsmb/smb_pass.h>
69 * The smb_ptd is a cache of Uid's, User names, passwords and domain names.
70 * It will be used for storing the password information for a user and will
71 * be used to for connections without entering the pasword again if its
72 * already keyed in by the user. Its a kind of Key-Chain mechanism
73 * implemented by Apple folks.
77 * Information stored in the nodes:
78 * UID: Uid of the person who initiated the login request.
79 * ZoneID: ZoneID of the zone from where the login request is initiated.
80 * Username: Username in the CIFS server.
81 * Srvdom: Domain name/ Server name of the CIFS server.
82 * Password: Password of the user.
83 * For more information, see smb_pass.h and sys/avl.h
87 * Information retrieved from the node.
88 * Node/password information can only be retrived with a call
89 * to smb_pkey_getpw(). Password never gets copied to the userspace.
90 * It will be copied to the Kernel data structure smbioc_ossn->ioc_password
91 * when needed for doing the "Session Setup". All other calls will return
92 * either a success or a failure.
95 avl_tree_t smb_ptd; /* AVL password tree descriptor */
96 unsigned int smb_list_len = 0; /* No. of elements in the tree. */
97 kmutex_t smb_ptd_lock; /* Mutex lock for controlled access */
99 int smb_pkey_check(smbioc_pk_t *pk, cred_t *cr);
100 int smb_pkey_deluid(uid_t ioc_uid, cred_t *cr);
103 * This routine is called by AVL tree calls when they want to find a
104 * node, find the next position in the tree to add or for deletion.
105 * Compare nodes from the tree to find the actual node based on
106 * uid/zoneid/username/domainname.
109 smb_pkey_cmp(const void *a, const void *b)
111 const smb_passid_t *pa = (smb_passid_t *)a;
112 const smb_passid_t *pb = (smb_passid_t *)b;
113 int duser, dsrv, error;
115 ASSERT(MUTEX_HELD(&smb_ptd_lock));
118 * The nodes are added sorted on the uid/zoneid/domainname/username
119 * We will do this:
120 * Compare uid's. The owner who stored the node gets access.
121 * Then zoneid to check if the access is from the same zone.
122 * Compare usernames.
123 * If the above are same, then compare domain/server names.
125 if (pa->uid < pb->uid)
126 return (-1);
127 if (pa->uid > pb->uid)
128 return (+1);
129 if (pa->zoneid < pb->zoneid)
130 return (-1);
131 if (pa->zoneid > pb->zoneid)
132 return (+1);
133 dsrv = u8_strcmp(pa->srvdom, pb->srvdom, 0,
134 U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &error);
135 if (dsrv < 0)
136 return (-1);
137 if (dsrv > 0)
138 return (+1);
139 duser = u8_strcmp(pa->username, pb->username, 0,
140 U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &error);
141 if (duser < 0)
142 return (-1);
143 if (duser > 0)
144 return (+1);
145 return (0);
149 * Initialization of the code that deals with uid and passwords.
151 void
152 smb_pkey_init()
154 avl_create(&smb_ptd,
155 smb_pkey_cmp,
156 sizeof (smb_passid_t),
157 offsetof(smb_passid_t,
158 cpnode));
159 mutex_init(&smb_ptd_lock, NULL, MUTEX_DEFAULT, NULL);
163 * Destroy the full AVL tree.
164 * Called just before unload.
166 void
167 smb_pkey_fini()
169 (void) smb_pkey_deluid((uid_t)-1, kcred);
170 avl_destroy(&smb_ptd);
171 mutex_destroy(&smb_ptd_lock);
175 * Driver unload calls this to ask if we
176 * have any stored passwords
179 smb_pkey_idle()
181 int n;
183 mutex_enter(&smb_ptd_lock);
184 n = avl_numnodes(&smb_ptd);
185 mutex_exit(&smb_ptd_lock);
187 return ((n) ? EBUSY : 0);
190 static void
191 smb_pkey_delete(smb_passid_t *tmp)
193 ASSERT(MUTEX_HELD(&smb_ptd_lock));
194 avl_remove(&smb_ptd, tmp);
195 strfree(tmp->srvdom);
196 strfree(tmp->username);
197 kmem_free(tmp, sizeof (*tmp));
202 * Remove a node from the AVL tree identified by cpid.
205 smb_pkey_del(smbioc_pk_t *pk, cred_t *cr)
207 avl_index_t where;
208 smb_passid_t buf, *cpid, *tmp;
209 uid_t uid;
211 tmp = &buf;
212 uid = pk->pk_uid;
213 if (uid == (uid_t)-1)
214 uid = crgetruid(cr);
215 else {
216 if (secpolicy_smbfs_login(cr, uid))
217 return (EPERM);
219 tmp->uid = uid;
220 tmp->zoneid = getzoneid();
221 tmp->srvdom = pk->pk_dom;
222 tmp->username = pk->pk_usr;
224 mutex_enter(&smb_ptd_lock);
225 if ((cpid = (smb_passid_t *)avl_find(&smb_ptd,
226 tmp, &where)) != NULL) {
227 smb_pkey_delete(cpid);
229 mutex_exit(&smb_ptd_lock);
231 return (0);
235 * Delete the entries owned by a particular user
236 * based on uid. We go through all the nodes and
237 * delete the nodes whereever the uid matches.
239 * Also implements "delete all" when uid == -1.
241 * You must have privilege to use any uid other
242 * than your real uid.
245 smb_pkey_deluid(uid_t ioc_uid, cred_t *cr)
247 smb_passid_t *cpid, *tmp;
249 if (secpolicy_smbfs_login(cr, ioc_uid))
250 return (EPERM);
252 mutex_enter(&smb_ptd_lock);
253 for (tmp = avl_first(&smb_ptd); tmp != NULL;
254 tmp = cpid) {
255 cpid = AVL_NEXT(&smb_ptd, tmp);
256 if (ioc_uid == (uid_t)-1 ||
257 ioc_uid == tmp->uid) {
259 * Delete the node.
261 smb_pkey_delete(tmp);
264 mutex_exit(&smb_ptd_lock);
266 return (0);
270 * Add entry or modify existing.
271 * Check for existing entry..
272 * If present, delete.
273 * Now, add the new entry.
276 smb_pkey_add(smbioc_pk_t *pk, cred_t *cr)
278 avl_tree_t *t = &smb_ptd;
279 avl_index_t where;
280 smb_passid_t *tmp, *cpid;
281 int ret;
282 uid_t uid;
284 uid = pk->pk_uid;
285 if (uid == (uid_t)-1)
286 uid = crgetruid(cr);
287 else {
288 if (secpolicy_smbfs_login(cr, uid))
289 return (EPERM);
291 cpid = kmem_zalloc(sizeof (smb_passid_t), KM_SLEEP);
292 cpid->uid = uid;
293 cpid->zoneid = getzoneid();
294 cpid->srvdom = strdup(pk->pk_dom);
295 cpid->username = strdup(pk->pk_usr);
296 bcopy(pk->pk_lmhash, cpid->lmhash, SMBIOC_HASH_SZ);
297 bcopy(pk->pk_nthash, cpid->nthash, SMBIOC_HASH_SZ);
300 * XXX: Instead of calling smb_pkey_check here,
301 * should call avl_find directly, and hold the
302 * lock across: avl_find, avl_remove, avl_insert.
305 /* If it already exists, delete it. */
306 ret = smb_pkey_check(pk, cr);
307 if (ret == 0) {
308 (void) smb_pkey_del(pk, cr);
311 mutex_enter(&smb_ptd_lock);
312 tmp = (smb_passid_t *)avl_find(t, cpid, &where);
313 if (tmp == NULL) {
314 avl_insert(t, cpid, where);
315 } else {
316 strfree(cpid->srvdom);
317 strfree(cpid->username);
318 kmem_free(cpid, sizeof (smb_passid_t));
320 mutex_exit(&smb_ptd_lock);
322 return (0);
326 * Determine if a node with uid,zoneid, uname & dname exists in the tree
327 * given the information, and if found, return the hashes.
330 smb_pkey_check(smbioc_pk_t *pk, cred_t *cr)
332 avl_tree_t *t = &smb_ptd;
333 avl_index_t where;
334 smb_passid_t *tmp, *cpid;
335 int error = ENOENT;
336 uid_t uid;
338 uid = pk->pk_uid;
339 if (uid == (uid_t)-1)
340 uid = crgetruid(cr);
341 else {
342 if (secpolicy_smbfs_login(cr, uid))
343 return (EPERM);
345 cpid = kmem_alloc(sizeof (smb_passid_t), KM_SLEEP);
346 cpid->uid = uid;
347 cpid->zoneid = getzoneid();
348 cpid->srvdom = pk->pk_dom;
349 cpid->username = pk->pk_usr;
351 mutex_enter(&smb_ptd_lock);
352 tmp = (smb_passid_t *)avl_find(t, cpid, &where);
353 mutex_exit(&smb_ptd_lock);
355 if (tmp != NULL) {
356 bcopy(tmp->lmhash, pk->pk_lmhash, SMBIOC_HASH_SZ);
357 bcopy(tmp->nthash, pk->pk_nthash, SMBIOC_HASH_SZ);
358 error = 0;
361 kmem_free(cpid, sizeof (smb_passid_t));
362 return (error);
367 smb_pkey_ioctl(int cmd, intptr_t arg, int flags, cred_t *cr)
369 smbioc_pk_t *pk;
370 uid_t uid;
371 int err = 0;
373 pk = kmem_alloc(sizeof (*pk), KM_SLEEP);
375 switch (cmd) {
376 case SMBIOC_PK_ADD:
377 case SMBIOC_PK_DEL:
378 case SMBIOC_PK_CHK:
379 if (ddi_copyin((void *)arg, pk,
380 sizeof (*pk), flags)) {
381 err = EFAULT;
382 goto out;
384 /* Make strlen (etc) on these safe. */
385 pk->pk_dom[SMBIOC_MAX_NAME-1] = '\0';
386 pk->pk_usr[SMBIOC_MAX_NAME-1] = '\0';
387 break;
390 switch (cmd) {
391 case SMBIOC_PK_ADD:
392 err = smb_pkey_add(pk, cr);
393 break;
395 case SMBIOC_PK_DEL:
396 err = smb_pkey_del(pk, cr);
397 break;
399 case SMBIOC_PK_CHK:
400 err = smb_pkey_check(pk, cr);
401 /* This is just a hash now. */
402 (void) ddi_copyout(pk, (void *)arg,
403 sizeof (*pk), flags);
404 break;
406 case SMBIOC_PK_DEL_OWNER:
407 uid = crgetruid(cr);
408 err = smb_pkey_deluid(uid, cr);
409 break;
411 case SMBIOC_PK_DEL_EVERYONE:
412 uid = (uid_t)-1;
413 err = smb_pkey_deluid(uid, cr);
414 break;
416 default:
417 err = ENODEV;
420 out:
421 kmem_free(pk, sizeof (*pk));
422 return (err);