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 * Password Keychain storage mechanism. 29 */ 30 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> 59 60 #include <netsmb/smb_osdep.h> 61 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> 67 68 /* 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. 74 */ 75 76 /* 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 84 */ 85 86 /* 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. 93 */ 94 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 */ 98 99 int smb_pkey_check(smbioc_pk_t *pk, cred_t *cr); 100 int smb_pkey_deluid(uid_t ioc_uid, cred_t *cr); 101 102 /* 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. 107 */ 108 int 109 smb_pkey_cmp(const void *a, const void *b) 110 { 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; 114 115 ASSERT(MUTEX_HELD(&smb_ptd_lock)); 116 117 /* 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. 124 */ 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); 146 } 147 148 /* 149 * Initialization of the code that deals with uid and passwords. 150 */ 151 void 152 smb_pkey_init() 153 { 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); 160 } 161 162 /* 163 * Destroy the full AVL tree. 164 * Called just before unload. 165 */ 166 void 167 smb_pkey_fini() 168 { 169 (void) smb_pkey_deluid((uid_t)-1, kcred); 170 avl_destroy(&smb_ptd); 171 mutex_destroy(&smb_ptd_lock); 172 } 173 174 /* 175 * Driver unload calls this to ask if we 176 * have any stored passwords 177 */ 178 int 179 smb_pkey_idle() 180 { 181 int n; 182 183 mutex_enter(&smb_ptd_lock); 184 n = avl_numnodes(&smb_ptd); 185 mutex_exit(&smb_ptd_lock); 186 187 return ((n) ? EBUSY : 0); 188 } 189 190 static void 191 smb_pkey_delete(smb_passid_t *tmp) 192 { 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)); 198 } 199 200 201 /* 202 * Remove a node from the AVL tree identified by cpid. 203 */ 204 int 205 smb_pkey_del(smbioc_pk_t *pk, cred_t *cr) 206 { 207 avl_index_t where; 208 smb_passid_t buf, *cpid, *tmp; 209 uid_t uid; 210 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); 218 } 219 tmp->uid = uid; 220 tmp->zoneid = getzoneid(); 221 tmp->srvdom = pk->pk_dom; 222 tmp->username = pk->pk_usr; 223 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); 228 } 229 mutex_exit(&smb_ptd_lock); 230 231 return (0); 232 } 233 234 /* 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. 238 * 239 * Also implements "delete all" when uid == -1. 240 * 241 * You must have privilege to use any uid other 242 * than your real uid. 243 */ 244 int 245 smb_pkey_deluid(uid_t ioc_uid, cred_t *cr) 246 { 247 smb_passid_t *cpid, *tmp; 248 249 if (secpolicy_smbfs_login(cr, ioc_uid)) 250 return (EPERM); 251 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) { 258 /* 259 * Delete the node. 260 */ 261 smb_pkey_delete(tmp); 262 } 263 } 264 mutex_exit(&smb_ptd_lock); 265 266 return (0); 267 } 268 269 /* 270 * Add entry or modify existing. 271 * Check for existing entry.. 272 * If present, delete. 273 * Now, add the new entry. 274 */ 275 int 276 smb_pkey_add(smbioc_pk_t *pk, cred_t *cr) 277 { 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; 283 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); 290 } 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); 298 299 /* 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. 303 */ 304 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); 309 } 310 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)); 319 } 320 mutex_exit(&smb_ptd_lock); 321 322 return (0); 323 } 324 325 /* 326 * Determine if a node with uid,zoneid, uname & dname exists in the tree 327 * given the information, and if found, return the hashes. 328 */ 329 int 330 smb_pkey_check(smbioc_pk_t *pk, cred_t *cr) 331 { 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; 337 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); 344 } 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; 350 351 mutex_enter(&smb_ptd_lock); 352 tmp = (smb_passid_t *)avl_find(t, cpid, &where); 353 mutex_exit(&smb_ptd_lock); 354 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; 359 } 360 361 kmem_free(cpid, sizeof (smb_passid_t)); 362 return (error); 363 } 364 365 366 int 367 smb_pkey_ioctl(int cmd, intptr_t arg, int flags, cred_t *cr) 368 { 369 smbioc_pk_t *pk; 370 uid_t uid; 371 int err = 0; 372 373 pk = kmem_alloc(sizeof (*pk), KM_SLEEP); 374 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; 383 } 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; 388 } 389 390 switch (cmd) { 391 case SMBIOC_PK_ADD: 392 err = smb_pkey_add(pk, cr); 393 break; 394 395 case SMBIOC_PK_DEL: 396 err = smb_pkey_del(pk, cr); 397 break; 398 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; 405 406 case SMBIOC_PK_DEL_OWNER: 407 uid = crgetruid(cr); 408 err = smb_pkey_deluid(uid, cr); 409 break; 410 411 case SMBIOC_PK_DEL_EVERYONE: 412 uid = (uid_t)-1; 413 err = smb_pkey_deluid(uid, cr); 414 break; 415 416 default: 417 err = ENODEV; 418 } 419 420 out: 421 kmem_free(pk, sizeof (*pk)); 422 return (err); 423 } 424