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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Password Keychain storage mechanism. 31 */ 32 33 #include <sys/types.h> 34 #include <sys/param.h> 35 #include <sys/errno.h> 36 #include <sys/sysmacros.h> 37 #include <sys/uio.h> 38 #include <sys/buf.h> 39 #include <sys/modctl.h> 40 #include <sys/open.h> 41 #include <sys/file.h> 42 #include <sys/kmem.h> 43 #include <sys/conf.h> 44 #include <sys/cmn_err.h> 45 #include <sys/stat.h> 46 #include <sys/ddi.h> 47 #include <sys/sunddi.h> 48 #include <sys/sunldi.h> 49 #include <sys/policy.h> 50 #include <sys/zone.h> 51 #include <sys/pathname.h> 52 #include <sys/mount.h> 53 #include <sys/sdt.h> 54 #include <fs/fs_subr.h> 55 #include <sys/devops.h> 56 #include <sys/thread.h> 57 #include <sys/mkdev.h> 58 #include <sys/avl.h> 59 #include <sys/avl_impl.h> 60 61 #include <netsmb/smb_osdep.h> 62 63 #include <netsmb/smb.h> 64 #include <netsmb/smb_conn.h> 65 #include <netsmb/smb_subr.h> 66 #include <netsmb/smb_dev.h> 67 #include <netsmb/smb_pass.h> 68 69 /* 70 * The smb_ptd is a cache of Uid's, User names, passwords and domain names. 71 * It will be used for storing the password information for a user and will 72 * be used to for connections without entering the pasword again if its 73 * already keyed in by the user. Its a kind of Key-Chain mechanism 74 * implemented by Apple folks. 75 */ 76 77 /* 78 * Information stored in the nodes: 79 * UID: Uid of the person who initiated the login request. 80 * ZoneID: ZoneID of the zone from where the login request is initiated. 81 * Username: Username in the CIFS server. 82 * Srvdom: Domain name/ Server name of the CIFS server. 83 * Password: Password of the user. 84 * For more information, see smb_pass.h and sys/avl.h 85 */ 86 87 /* 88 * Information retrieved from the node. 89 * Node/password information can only be retrived with a call 90 * to smb_pkey_getpw(). Password never gets copied to the userspace. 91 * It will be copied to the Kernel data structure smbioc_ossn->ioc_password 92 * when needed for doing the "Session Setup". All other calls will return 93 * either a success or a failure. 94 */ 95 96 avl_tree_t smb_ptd; /* AVL password tree descriptor */ 97 unsigned int smb_list_len = 0; /* No. of elements in the tree. */ 98 kmutex_t smb_ptd_lock; /* Mutex lock for controlled access */ 99 100 /* 101 * This routine is called by AVL tree calls when they want to find a 102 * node, find the next position in the tree to add or for deletion. 103 * Compare nodes from the tree to find the actual node based on 104 * uid/zoneid/username/domainname. 105 */ 106 int 107 smb_pkey_cmp(const void *a, const void *b) 108 { 109 const smb_passid_t *pa = (smb_passid_t *)a; 110 const smb_passid_t *pb = (smb_passid_t *)b; 111 int duser, dsrv; 112 113 ASSERT(MUTEX_HELD(&smb_ptd_lock)); 114 115 /* 116 * The nodes are added sorted on the uid/zoneid/domainname/username 117 * We will do this: 118 * Compare uid's. The owner who stored the node gets access. 119 * Then zoneid to check if the access is from the same zone. 120 * Compare usernames. 121 * If the above are same, then compare domain/server names. 122 */ 123 if (pa->uid < pb->uid) 124 return (-1); 125 if (pa->uid > pb->uid) 126 return (+1); 127 if (pa->zoneid < pb->zoneid) 128 return (-1); 129 if (pa->zoneid > pb->zoneid) 130 return (+1); 131 dsrv = strcasecmp(pa->srvdom, pb->srvdom); 132 if (dsrv < 0) 133 return (-1); 134 if (dsrv > 0) 135 return (+1); 136 duser = strcasecmp(pa->username, pb->username); 137 if (duser < 0) 138 return (-1); 139 if (duser > 0) 140 return (+1); 141 return (0); 142 } 143 144 /* 145 * Initialization of the code that deals with uid and passwords. 146 */ 147 void 148 smb_pkey_init() 149 { 150 avl_create(&smb_ptd, 151 smb_pkey_cmp, 152 sizeof (smb_passid_t), 153 offsetof(smb_passid_t, 154 cpnode)); 155 mutex_init(&smb_ptd_lock, NULL, MUTEX_DEFAULT, NULL); 156 } 157 158 /* 159 * Destroy the full AVL tree. 160 */ 161 void 162 smb_pkey_fini() 163 { 164 smb_pkey_deluid((uid_t)-1, CRED()); 165 avl_destroy(&smb_ptd); 166 mutex_destroy(&smb_ptd_lock); 167 } 168 169 /* 170 * Driver unload calls this to ask if we 171 * have any stored passwords 172 */ 173 int 174 smb_pkey_idle() 175 { 176 int n; 177 178 mutex_enter(&smb_ptd_lock); 179 n = avl_numnodes(&smb_ptd); 180 mutex_exit(&smb_ptd_lock); 181 182 return ((n) ? EBUSY : 0); 183 } 184 185 int 186 smb_node_delete(smb_passid_t *tmp) 187 { 188 ASSERT(MUTEX_HELD(&smb_ptd_lock)); 189 avl_remove(&smb_ptd, tmp); 190 smb_strfree(tmp->srvdom); 191 smb_strfree(tmp->username); 192 kmem_free(tmp, sizeof (*tmp)); 193 return (0); 194 } 195 196 197 /* 198 * Remove a node from the AVL tree identified by cpid. 199 */ 200 int 201 smb_pkey_del(smbioc_pk_t *pk, cred_t *cr) 202 { 203 avl_index_t where; 204 smb_passid_t buf, *cpid, *tmp; 205 uid_t uid; 206 207 tmp = &buf; 208 uid = pk->pk_uid; 209 if (uid == (uid_t)-1) 210 uid = crgetruid(cr); 211 else { 212 if (secpolicy_smbfs_login(cr, uid)) 213 return (EPERM); 214 } 215 tmp->uid = uid; 216 tmp->zoneid = getzoneid(); 217 tmp->srvdom = pk->pk_dom; 218 tmp->username = pk->pk_usr; 219 220 mutex_enter(&smb_ptd_lock); 221 if ((cpid = (smb_passid_t *)avl_find(&smb_ptd, 222 tmp, &where)) != NULL) { 223 smb_node_delete(cpid); 224 } 225 mutex_exit(&smb_ptd_lock); 226 227 return (0); 228 } 229 230 /* 231 * Delete the entries owned by a particular user 232 * based on uid. We go through all the nodes and 233 * delete the nodes whereever the uid matches. 234 * 235 * Also implements "delete all" when uid == -1. 236 * 237 * You must have privilege to use any uid other 238 * than your real uid. 239 */ 240 int 241 smb_pkey_deluid(uid_t ioc_uid, cred_t *cr) 242 { 243 smb_passid_t *cpid, *tmp; 244 245 if (secpolicy_smbfs_login(cr, ioc_uid)) 246 return (EPERM); 247 248 mutex_enter(&smb_ptd_lock); 249 for (tmp = avl_first(&smb_ptd); tmp != NULL; 250 tmp = cpid) { 251 cpid = AVL_NEXT(&smb_ptd, tmp); 252 if (ioc_uid == (uid_t)-1 || 253 ioc_uid == tmp->uid) { 254 /* 255 * Delete the node. 256 */ 257 smb_node_delete(tmp); 258 } 259 } 260 mutex_exit(&smb_ptd_lock); 261 262 return (0); 263 } 264 265 /* 266 * Add entry or modify existing. 267 * Check for existing entry.. 268 * If present, delete. 269 * Now, add the new entry. 270 */ 271 int 272 smb_pkey_add(smbioc_pk_t *pk, cred_t *cr) 273 { 274 avl_tree_t *t = &smb_ptd; 275 avl_index_t where; 276 smb_passid_t *tmp, *cpid; 277 int ret; 278 uid_t uid; 279 280 uid = pk->pk_uid; 281 if (uid == (uid_t)-1) 282 uid = crgetruid(cr); 283 else { 284 if (secpolicy_smbfs_login(cr, uid)) 285 return (EPERM); 286 } 287 cpid = kmem_zalloc(sizeof (smb_passid_t), KM_SLEEP); 288 cpid->uid = uid; 289 cpid->zoneid = getzoneid(); 290 cpid->srvdom = smb_strdup(pk->pk_dom); 291 cpid->username = smb_strdup(pk->pk_usr); 292 smb_oldlm_hash(pk->pk_pass, cpid->lmhash); 293 smb_ntlmv1hash(pk->pk_pass, cpid->nthash); 294 295 /* 296 * XXX: Instead of calling smb_pkey_check here, 297 * should call avl_find directly, and hold the 298 * lock across: avl_find, avl_remove, avl_insert. 299 */ 300 301 /* If it already exists, delete it. */ 302 ret = smb_pkey_check(pk, cr); 303 if (ret == 0) { 304 smb_pkey_del(pk, cr); 305 } 306 307 mutex_enter(&smb_ptd_lock); 308 tmp = (smb_passid_t *)avl_find(t, cpid, &where); 309 if (tmp == NULL) { 310 avl_insert(t, cpid, where); 311 } else { 312 smb_strfree(cpid->srvdom); 313 smb_strfree(cpid->username); 314 kmem_free(cpid, sizeof (smb_passid_t)); 315 } 316 mutex_exit(&smb_ptd_lock); 317 318 return (0); 319 } 320 321 /* 322 * Determine if a node with uid,zoneid, uname & dname exists in the tree 323 * given the information. Does NOT return the stored password. 324 */ 325 int 326 smb_pkey_check(smbioc_pk_t *pk, cred_t *cr) 327 { 328 avl_tree_t *t = &smb_ptd; 329 avl_index_t where; 330 smb_passid_t *tmp, *cpid; 331 int error = ENOENT; 332 uid_t uid; 333 334 uid = pk->pk_uid; 335 if (uid == (uid_t)-1) 336 uid = crgetruid(cr); 337 else { 338 if (secpolicy_smbfs_login(cr, uid)) 339 return (EPERM); 340 } 341 cpid = kmem_alloc(sizeof (smb_passid_t), KM_SLEEP); 342 cpid->uid = uid; 343 cpid->zoneid = getzoneid(); 344 cpid->srvdom = pk->pk_dom; 345 cpid->username = pk->pk_usr; 346 347 mutex_enter(&smb_ptd_lock); 348 tmp = (smb_passid_t *)avl_find(t, cpid, &where); 349 if (tmp != NULL) 350 error = 0; 351 mutex_exit(&smb_ptd_lock); 352 353 kmem_free(cpid, sizeof (smb_passid_t)); 354 return (error); 355 } 356 357 /* 358 * Interface function between the keychain mechanism and SMB password 359 * handling during Session Setup. Internal form of smb_pkey_check(). 360 * Copies the password hashes into the VC. 361 */ 362 int 363 smb_pkey_getpwh(struct smb_vc *vcp, cred_t *cr) 364 { 365 avl_tree_t *t = &smb_ptd; 366 avl_index_t where; 367 smb_passid_t *tmp, *cpid; 368 int error = ENOENT; 369 370 cpid = kmem_alloc(sizeof (smb_passid_t), KM_SLEEP); 371 cpid->uid = crgetruid(cr); 372 cpid->zoneid = getzoneid(); 373 cpid->username = vcp->vc_username; 374 375 if (vcp->vc_vopt & SMBVOPT_KC_DOMAIN) 376 cpid->srvdom = vcp->vc_domain; 377 else 378 cpid->srvdom = vcp->vc_srvname; 379 380 mutex_enter(&smb_ptd_lock); 381 tmp = (smb_passid_t *)avl_find(t, cpid, &where); 382 if (tmp != NULL) { 383 bcopy(tmp->lmhash, vcp->vc_lmhash, SMB_PWH_MAX); 384 bcopy(tmp->nthash, vcp->vc_nthash, SMB_PWH_MAX); 385 error = 0; 386 } 387 mutex_exit(&smb_ptd_lock); 388 389 kmem_free(cpid, sizeof (smb_passid_t)); 390 return (error); 391 } 392