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 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 int 191 smb_node_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 return (0); 199 } 200 201 202 /* 203 * Remove a node from the AVL tree identified by cpid. 204 */ 205 int 206 smb_pkey_del(smbioc_pk_t *pk, cred_t *cr) 207 { 208 avl_index_t where; 209 smb_passid_t buf, *cpid, *tmp; 210 uid_t uid; 211 212 tmp = &buf; 213 uid = pk->pk_uid; 214 if (uid == (uid_t)-1) 215 uid = crgetruid(cr); 216 else { 217 if (secpolicy_smbfs_login(cr, uid)) 218 return (EPERM); 219 } 220 tmp->uid = uid; 221 tmp->zoneid = getzoneid(); 222 tmp->srvdom = pk->pk_dom; 223 tmp->username = pk->pk_usr; 224 225 mutex_enter(&smb_ptd_lock); 226 if ((cpid = (smb_passid_t *)avl_find(&smb_ptd, 227 tmp, &where)) != NULL) { 228 smb_node_delete(cpid); 229 } 230 mutex_exit(&smb_ptd_lock); 231 232 return (0); 233 } 234 235 /* 236 * Delete the entries owned by a particular user 237 * based on uid. We go through all the nodes and 238 * delete the nodes whereever the uid matches. 239 * 240 * Also implements "delete all" when uid == -1. 241 * 242 * You must have privilege to use any uid other 243 * than your real uid. 244 */ 245 int 246 smb_pkey_deluid(uid_t ioc_uid, cred_t *cr) 247 { 248 smb_passid_t *cpid, *tmp; 249 250 if (secpolicy_smbfs_login(cr, ioc_uid)) 251 return (EPERM); 252 253 mutex_enter(&smb_ptd_lock); 254 for (tmp = avl_first(&smb_ptd); tmp != NULL; 255 tmp = cpid) { 256 cpid = AVL_NEXT(&smb_ptd, tmp); 257 if (ioc_uid == (uid_t)-1 || 258 ioc_uid == tmp->uid) { 259 /* 260 * Delete the node. 261 */ 262 smb_node_delete(tmp); 263 } 264 } 265 mutex_exit(&smb_ptd_lock); 266 267 return (0); 268 } 269 270 /* 271 * Add entry or modify existing. 272 * Check for existing entry.. 273 * If present, delete. 274 * Now, add the new entry. 275 */ 276 int 277 smb_pkey_add(smbioc_pk_t *pk, cred_t *cr) 278 { 279 avl_tree_t *t = &smb_ptd; 280 avl_index_t where; 281 smb_passid_t *tmp, *cpid; 282 int ret; 283 uid_t uid; 284 285 uid = pk->pk_uid; 286 if (uid == (uid_t)-1) 287 uid = crgetruid(cr); 288 else { 289 if (secpolicy_smbfs_login(cr, uid)) 290 return (EPERM); 291 } 292 cpid = kmem_zalloc(sizeof (smb_passid_t), KM_SLEEP); 293 cpid->uid = uid; 294 cpid->zoneid = getzoneid(); 295 cpid->srvdom = strdup(pk->pk_dom); 296 cpid->username = strdup(pk->pk_usr); 297 bcopy(pk->pk_lmhash, cpid->lmhash, SMBIOC_HASH_SZ); 298 bcopy(pk->pk_nthash, cpid->nthash, SMBIOC_HASH_SZ); 299 300 /* 301 * XXX: Instead of calling smb_pkey_check here, 302 * should call avl_find directly, and hold the 303 * lock across: avl_find, avl_remove, avl_insert. 304 */ 305 306 /* If it already exists, delete it. */ 307 ret = smb_pkey_check(pk, cr); 308 if (ret == 0) { 309 smb_pkey_del(pk, cr); 310 } 311 312 mutex_enter(&smb_ptd_lock); 313 tmp = (smb_passid_t *)avl_find(t, cpid, &where); 314 if (tmp == NULL) { 315 avl_insert(t, cpid, where); 316 } else { 317 strfree(cpid->srvdom); 318 strfree(cpid->username); 319 kmem_free(cpid, sizeof (smb_passid_t)); 320 } 321 mutex_exit(&smb_ptd_lock); 322 323 return (0); 324 } 325 326 /* 327 * Determine if a node with uid,zoneid, uname & dname exists in the tree 328 * given the information, and if found, return the hashes. 329 */ 330 int 331 smb_pkey_check(smbioc_pk_t *pk, cred_t *cr) 332 { 333 avl_tree_t *t = &smb_ptd; 334 avl_index_t where; 335 smb_passid_t *tmp, *cpid; 336 int error = ENOENT; 337 uid_t uid; 338 339 uid = pk->pk_uid; 340 if (uid == (uid_t)-1) 341 uid = crgetruid(cr); 342 else { 343 if (secpolicy_smbfs_login(cr, uid)) 344 return (EPERM); 345 } 346 cpid = kmem_alloc(sizeof (smb_passid_t), KM_SLEEP); 347 cpid->uid = uid; 348 cpid->zoneid = getzoneid(); 349 cpid->srvdom = pk->pk_dom; 350 cpid->username = pk->pk_usr; 351 352 mutex_enter(&smb_ptd_lock); 353 tmp = (smb_passid_t *)avl_find(t, cpid, &where); 354 mutex_exit(&smb_ptd_lock); 355 356 if (tmp != NULL) { 357 bcopy(tmp->lmhash, pk->pk_lmhash, SMBIOC_HASH_SZ); 358 bcopy(tmp->nthash, pk->pk_nthash, SMBIOC_HASH_SZ); 359 error = 0; 360 } 361 362 kmem_free(cpid, sizeof (smb_passid_t)); 363 return (error); 364 } 365 366 367 int 368 smb_pkey_ioctl(int cmd, intptr_t arg, int flags, cred_t *cr) 369 { 370 smbioc_pk_t *pk; 371 uid_t uid; 372 int err = 0; 373 374 pk = kmem_alloc(sizeof (*pk), KM_SLEEP); 375 376 switch (cmd) { 377 case SMBIOC_PK_ADD: 378 case SMBIOC_PK_DEL: 379 case SMBIOC_PK_CHK: 380 if (ddi_copyin((void *)arg, pk, 381 sizeof (*pk), flags)) { 382 err = EFAULT; 383 goto out; 384 } 385 /* Make strlen (etc) on these safe. */ 386 pk->pk_dom[SMBIOC_MAX_NAME-1] = '\0'; 387 pk->pk_usr[SMBIOC_MAX_NAME-1] = '\0'; 388 break; 389 } 390 391 switch (cmd) { 392 case SMBIOC_PK_ADD: 393 err = smb_pkey_add(pk, cr); 394 break; 395 396 case SMBIOC_PK_DEL: 397 err = smb_pkey_del(pk, cr); 398 break; 399 400 case SMBIOC_PK_CHK: 401 err = smb_pkey_check(pk, cr); 402 /* This is just a hash now. */ 403 (void) ddi_copyout(pk, (void *)arg, 404 sizeof (*pk), flags); 405 break; 406 407 case SMBIOC_PK_DEL_OWNER: 408 uid = crgetruid(cr); 409 err = smb_pkey_deluid(uid, cr); 410 break; 411 412 case SMBIOC_PK_DEL_EVERYONE: 413 uid = (uid_t)-1; 414 err = smb_pkey_deluid(uid, cr); 415 break; 416 417 default: 418 err = ENODEV; 419 } 420 421 out: 422 kmem_free(pk, sizeof (*pk)); 423 return (err); 424 } 425