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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdlib.h> 29 #include <unistd.h> 30 #include <limits.h> 31 #include <strings.h> 32 #include <synch.h> 33 #include <errno.h> 34 #include <sys/types.h> 35 #include <sys/stat.h> 36 #include <fcntl.h> 37 #include <thread.h> 38 #include <pwd.h> 39 #include <dlfcn.h> 40 #include <link.h> 41 #include <smbsrv/libsmb.h> 42 43 #define SMB_PASSWD "/var/smb/smbpasswd" 44 #define SMB_OPASSWD "/var/smb/osmbpasswd" 45 #define SMB_PASSTEMP "/var/smb/ptmp" 46 #define SMB_PASSLCK "/var/smb/.pwd.lock" 47 #define SMB_LIBPALT "/usr/lib/smbsrv" 48 #define SMB_LIBNALT "libsmb_pwd.so" 49 #define SMB_LIB_ALT SMB_LIBPALT "/" SMB_LIBNALT 50 51 #define SMB_PWD_DISABLE "*DIS*" 52 #define SMB_PWD_BUFSIZE 256 53 54 #define S_WAITTIME 15 55 56 typedef enum { 57 SMB_PWD_NAME = 0, 58 SMB_PWD_UID, 59 SMB_PWD_LMHASH, 60 SMB_PWD_NTHASH, 61 SMB_PWD_NARG 62 } smb_pwdarg_t; 63 64 static struct flock flock = { 65 0, /* l_type */ 66 0, /* l_whence */ 67 0, /* l_start */ 68 0, /* l_len */ 69 0, /* l_sysid */ 70 0 /* l_pid */ 71 }; 72 73 static pid_t lck_pid = 0; /* process's pid at last lock */ 74 static thread_t lck_tid = 0; /* thread that holds the lock */ 75 static int fildes = -1; 76 static mutex_t lck_lock = DEFAULTMUTEX; 77 static void *smb_pwd_hdl = NULL; 78 79 typedef struct smb_pwbuf { 80 char *pw_name; 81 smb_passwd_t *pw_pwd; 82 } smb_pwbuf_t; 83 84 static struct { 85 smb_passwd_t *(*smb_pwd_getpasswd)(const char *name, 86 smb_passwd_t *smbpw); 87 int (*smb_pwd_setcntl)(const char *name, int control); 88 int (*smb_pwd_setpasswd)(const char *name, const char *password); 89 } smb_pwd_ops; 90 91 static int smb_pwd_lock(void); 92 static int smb_pwd_unlock(void); 93 static int smb_pwd_flck(void); 94 static int smb_pwd_fulck(void); 95 96 static smb_pwbuf_t *smb_pwd_fgetent(FILE *, smb_pwbuf_t *, char *, size_t); 97 static int smb_pwd_fputent(FILE *, smb_pwbuf_t *); 98 static int smb_pwd_chgpwent(smb_passwd_t *, const char *, int); 99 static int smb_pwd_update(const char *, const char *, int); 100 101 void 102 smb_pwd_init(void) 103 { 104 smb_pwd_hdl = dlopen(SMB_LIB_ALT, RTLD_NOW | RTLD_LOCAL); 105 106 if (smb_pwd_hdl == NULL) 107 return; /* interposition library not found */ 108 109 bzero((void *)&smb_pwd_ops, sizeof (smb_pwd_ops)); 110 111 smb_pwd_ops.smb_pwd_getpasswd = 112 (smb_passwd_t *(*)())dlsym(smb_pwd_hdl, "smb_pwd_getpasswd"); 113 114 smb_pwd_ops.smb_pwd_setcntl = 115 (int (*)())dlsym(smb_pwd_hdl, "smb_pwd_setcntl"); 116 117 smb_pwd_ops.smb_pwd_setpasswd = 118 (int (*)())dlsym(smb_pwd_hdl, "smb_pwd_setpasswd"); 119 120 if (smb_pwd_ops.smb_pwd_getpasswd == NULL || 121 smb_pwd_ops.smb_pwd_setcntl == NULL || 122 smb_pwd_ops.smb_pwd_setpasswd == NULL) { 123 (void) dlclose(smb_pwd_hdl); 124 smb_pwd_hdl = NULL; 125 126 /* If error or function(s) are missing, use original lib */ 127 bzero((void *)&smb_pwd_ops, sizeof (smb_pwd_ops)); 128 } 129 } 130 131 void 132 smb_pwd_fini(void) 133 { 134 if (smb_pwd_hdl) { 135 (void) dlclose(smb_pwd_hdl); 136 smb_pwd_hdl = NULL; 137 } 138 } 139 140 /* 141 * smb_pwd_get 142 * 143 * Returns a smb password structure for the given user name. 144 * smbpw is a pointer to a buffer allocated by the caller. 145 * 146 * Returns NULL upon failure. 147 */ 148 smb_passwd_t * 149 smb_pwd_getpasswd(const char *name, smb_passwd_t *smbpw) 150 { 151 char buf[SMB_PWD_BUFSIZE]; 152 boolean_t found = B_FALSE; 153 smb_pwbuf_t pwbuf; 154 int err; 155 FILE *fp; 156 157 if (smb_pwd_ops.smb_pwd_getpasswd != NULL) 158 return (smb_pwd_ops.smb_pwd_getpasswd(name, smbpw)); 159 160 err = smb_pwd_lock(); 161 if (err != SMB_PWE_SUCCESS) 162 return (NULL); 163 164 if ((fp = fopen(SMB_PASSWD, "rF")) == NULL) { 165 (void) smb_pwd_unlock(); 166 return (NULL); 167 } 168 169 pwbuf.pw_name = NULL; 170 pwbuf.pw_pwd = smbpw; 171 172 while (smb_pwd_fgetent(fp, &pwbuf, buf, sizeof (buf)) != NULL) { 173 if (strcmp(name, pwbuf.pw_name) == 0) { 174 if ((smbpw->pw_flags & (SMB_PWF_LM | SMB_PWF_NT))) 175 found = B_TRUE; 176 break; 177 } 178 } 179 180 (void) fclose(fp); 181 (void) smb_pwd_unlock(); 182 183 if (!found) { 184 bzero(smbpw, sizeof (smb_passwd_t)); 185 return (NULL); 186 } 187 188 return (smbpw); 189 } 190 191 /* 192 * smb_pwd_set 193 * 194 * Update/add the given user to the smbpasswd file. 195 */ 196 int 197 smb_pwd_setpasswd(const char *name, const char *password) 198 { 199 if (smb_pwd_ops.smb_pwd_setpasswd != NULL) 200 return (smb_pwd_ops.smb_pwd_setpasswd(name, password)); 201 202 return (smb_pwd_update(name, password, 0)); 203 } 204 205 /* 206 * smb_pwd_setcntl 207 * 208 * Change the account state. This can be making the account 209 * disable/enable or removing its LM hash. 210 */ 211 int 212 smb_pwd_setcntl(const char *name, int control) 213 { 214 if (smb_pwd_ops.smb_pwd_setcntl != NULL) 215 return (smb_pwd_ops.smb_pwd_setcntl(name, control)); 216 217 if (control == 0) 218 return (SMB_PWE_SUCCESS); 219 220 return (smb_pwd_update(name, NULL, control)); 221 } 222 223 static int 224 smb_pwd_update(const char *name, const char *password, int control) 225 { 226 struct stat64 stbuf; 227 FILE *src, *dst; 228 int tempfd; 229 char buf[SMB_PWD_BUFSIZE]; 230 int err = SMB_PWE_SUCCESS; 231 smb_pwbuf_t pwbuf; 232 smb_passwd_t smbpw; 233 boolean_t newent = B_TRUE; 234 boolean_t user_disable = B_FALSE; 235 char uxbuf[1024]; 236 struct passwd uxpw; 237 int64_t lm_level; 238 239 err = smb_pwd_lock(); 240 if (err != SMB_PWE_SUCCESS) 241 return (err); 242 243 if (stat64(SMB_PASSWD, &stbuf) < 0) { 244 err = SMB_PWE_STAT_FAILED; 245 goto passwd_exit; 246 } 247 248 if ((tempfd = open(SMB_PASSTEMP, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) { 249 err = SMB_PWE_OPEN_FAILED; 250 goto passwd_exit; 251 } 252 253 if ((dst = fdopen(tempfd, "wF")) == NULL) { 254 err = SMB_PWE_OPEN_FAILED; 255 goto passwd_exit; 256 } 257 258 if ((src = fopen(SMB_PASSWD, "rF")) == NULL) { 259 err = SMB_PWE_OPEN_FAILED; 260 (void) fclose(dst); 261 (void) unlink(SMB_PASSTEMP); 262 goto passwd_exit; 263 } 264 265 if (smb_config_getnum(SMB_CI_LM_LEVEL, &lm_level) != SMBD_SMF_OK) 266 lm_level = 4; 267 268 if (lm_level >= 4) 269 control |= SMB_PWC_NOLM; 270 271 /* 272 * copy old password entries to temporary file while replacing 273 * the entry that matches "name" 274 */ 275 pwbuf.pw_name = NULL; 276 pwbuf.pw_pwd = &smbpw; 277 278 while (smb_pwd_fgetent(src, &pwbuf, buf, sizeof (buf)) != NULL) { 279 if (strcmp(pwbuf.pw_name, name) == 0) { 280 err = smb_pwd_chgpwent(&smbpw, password, control); 281 if (err == SMB_PWE_USER_DISABLE) 282 user_disable = B_TRUE; 283 err = smb_pwd_fputent(dst, &pwbuf); 284 newent = B_FALSE; 285 } else { 286 err = smb_pwd_fputent(dst, &pwbuf); 287 } 288 289 if (err != SMB_PWE_SUCCESS) { 290 (void) fclose(src); 291 (void) fclose(dst); 292 goto passwd_exit; 293 } 294 } 295 296 if (newent) { 297 if (getpwnam_r(name, &uxpw, uxbuf, sizeof (uxbuf))) { 298 pwbuf.pw_name = uxpw.pw_name; 299 smbpw.pw_flags = 0; 300 smbpw.pw_uid = uxpw.pw_uid; 301 (void) smb_pwd_chgpwent(&smbpw, password, control); 302 err = smb_pwd_fputent(dst, &pwbuf); 303 } else { 304 err = SMB_PWE_USER_UNKNOWN; 305 } 306 307 if (err != SMB_PWE_SUCCESS) { 308 (void) fclose(src); 309 (void) fclose(dst); 310 goto passwd_exit; 311 } 312 } 313 314 (void) fclose(src); 315 if (fclose(dst) != 0) { 316 err = SMB_PWE_CLOSE_FAILED; 317 goto passwd_exit; /* Don't trust the temporary file */ 318 } 319 320 /* Rename temp to passwd */ 321 if (unlink(SMB_OPASSWD) && access(SMB_OPASSWD, 0) == 0) { 322 err = SMB_PWE_UPDATE_FAILED; 323 (void) unlink(SMB_PASSTEMP); 324 goto passwd_exit; 325 } 326 327 if (link(SMB_PASSWD, SMB_OPASSWD) == -1) { 328 err = SMB_PWE_UPDATE_FAILED; 329 (void) unlink(SMB_PASSTEMP); 330 goto passwd_exit; 331 } 332 333 if (rename(SMB_PASSTEMP, SMB_PASSWD) == -1) { 334 err = SMB_PWE_UPDATE_FAILED; 335 (void) unlink(SMB_PASSTEMP); 336 goto passwd_exit; 337 } 338 339 (void) chmod(SMB_PASSWD, 0400); 340 341 passwd_exit: 342 (void) smb_pwd_unlock(); 343 if ((err == SMB_PWE_SUCCESS) && user_disable) 344 err = SMB_PWE_USER_DISABLE; 345 346 return (err); 347 } 348 349 /* 350 * smb_getpwent 351 * 352 * Parse the buffer in the passed pwbuf and fill in the 353 * smb password structure to point to the parsed information. 354 * The entry format is: 355 * 356 * <user-name>:<user-id>:<LM hash>:<NTLM hash> 357 * 358 * Returns a pointer to the password structure on success, 359 * otherwise returns NULL. 360 */ 361 static smb_pwbuf_t * 362 smb_pwd_fgetent(FILE *fp, smb_pwbuf_t *pwbuf, char *buf, size_t bufsize) 363 { 364 char *argv[SMB_PWD_NARG]; 365 smb_passwd_t *pw; 366 smb_pwdarg_t i; 367 int lm_len, nt_len; 368 369 if (fgets(buf, bufsize, fp) == NULL) 370 return (NULL); 371 (void) trim_whitespace(buf); 372 373 for (i = 0; i < SMB_PWD_NARG; ++i) { 374 if ((argv[i] = strsep((char **)&buf, ":")) == 0) { 375 return (NULL); 376 } 377 } 378 379 if ((*argv[SMB_PWD_NAME] == '\0') || (*argv[SMB_PWD_UID] == '\0')) 380 return (NULL); 381 382 pwbuf->pw_name = argv[SMB_PWD_NAME]; 383 pw = pwbuf->pw_pwd; 384 bzero(pw, sizeof (smb_passwd_t)); 385 pw->pw_uid = strtoul(argv[SMB_PWD_UID], 0, 10); 386 387 if (strcmp(argv[SMB_PWD_LMHASH], SMB_PWD_DISABLE) == 0) { 388 pw->pw_flags |= SMB_PWF_DISABLE; 389 (void) strcpy((char *)pw->pw_lmhash, SMB_PWD_DISABLE); 390 (void) strcpy((char *)pw->pw_nthash, SMB_PWD_DISABLE); 391 return (pwbuf); 392 } 393 394 lm_len = strlen(argv[SMB_PWD_LMHASH]); 395 if (lm_len == SMBAUTH_HEXHASH_SZ) { 396 (void) hextobin(argv[SMB_PWD_LMHASH], SMBAUTH_HEXHASH_SZ, 397 (char *)pw->pw_lmhash, SMBAUTH_HASH_SZ); 398 399 pw->pw_flags |= SMB_PWF_LM; 400 } else if (lm_len != 0) { 401 return (NULL); 402 } 403 404 nt_len = strlen(argv[SMB_PWD_NTHASH]); 405 if (nt_len == SMBAUTH_HEXHASH_SZ) { 406 (void) hextobin(argv[SMB_PWD_NTHASH], SMBAUTH_HEXHASH_SZ, 407 (char *)pw->pw_nthash, SMBAUTH_HASH_SZ); 408 409 pw->pw_flags |= SMB_PWF_NT; 410 } else if (nt_len != 0) { 411 return (NULL); 412 } 413 414 return (pwbuf); 415 } 416 417 static int 418 smb_pwd_chgpwent(smb_passwd_t *smbpw, const char *password, int control) 419 { 420 if (control & SMB_PWC_DISABLE) { 421 smbpw->pw_flags |= SMB_PWF_DISABLE; 422 (void) strcpy((char *)smbpw->pw_lmhash, SMB_PWD_DISABLE); 423 (void) strcpy((char *)smbpw->pw_nthash, SMB_PWD_DISABLE); 424 smbpw->pw_flags &= ~(SMB_PWF_LM | SMB_PWF_NT); 425 return (SMB_PWE_SUCCESS); 426 } else if ((control & SMB_PWC_ENABLE) && 427 (smbpw->pw_flags & SMB_PWF_DISABLE)) { 428 *smbpw->pw_lmhash = '\0'; 429 *smbpw->pw_nthash = '\0'; 430 smbpw->pw_flags &= ~(SMB_PWF_LM | SMB_PWF_NT); 431 return (SMB_PWE_SUCCESS); 432 } 433 434 /* No password update if account is disabled */ 435 if (smbpw->pw_flags & SMB_PWF_DISABLE) 436 return (SMB_PWE_USER_DISABLE); 437 438 if (control & SMB_PWC_NOLM) { 439 smbpw->pw_flags &= ~SMB_PWF_LM; 440 *smbpw->pw_lmhash = '\0'; 441 } else { 442 smbpw->pw_flags |= SMB_PWF_LM; 443 (void) smb_auth_lm_hash((char *)password, smbpw->pw_lmhash); 444 } 445 446 smbpw->pw_flags |= SMB_PWF_NT; 447 (void) smb_auth_ntlm_hash((char *)password, smbpw->pw_nthash); 448 return (SMB_PWE_SUCCESS); 449 } 450 451 /* 452 * smb_putpwent 453 * 454 * Creates LM and NTLM hash from the given plain text password 455 * and write them along with user's name and Id to the smbpasswd 456 * file. 457 */ 458 static int 459 smb_pwd_fputent(FILE *fp, smb_pwbuf_t *pwbuf) 460 { 461 smb_passwd_t *pw = pwbuf->pw_pwd; 462 char hex_nthash[SMBAUTH_HEXHASH_SZ+1]; 463 char hex_lmhash[SMBAUTH_HEXHASH_SZ+1]; 464 int rc; 465 466 if ((pw->pw_flags & SMB_PWF_LM) == SMB_PWF_LM) { 467 (void) bintohex((char *)pw->pw_lmhash, SMBAUTH_HASH_SZ, 468 hex_lmhash, SMBAUTH_HEXHASH_SZ); 469 hex_lmhash[SMBAUTH_HEXHASH_SZ] = '\0'; 470 } else { 471 (void) strcpy(hex_lmhash, (char *)pw->pw_lmhash); 472 } 473 474 if ((pw->pw_flags & SMB_PWF_NT) == SMB_PWF_NT) { 475 (void) bintohex((char *)pw->pw_nthash, SMBAUTH_HASH_SZ, 476 hex_nthash, SMBAUTH_HEXHASH_SZ); 477 hex_nthash[SMBAUTH_HEXHASH_SZ] = '\0'; 478 } else { 479 (void) strcpy(hex_nthash, (char *)pw->pw_nthash); 480 } 481 482 rc = fprintf(fp, "%s:%d:%s:%s\n", pwbuf->pw_name, pw->pw_uid, 483 hex_lmhash, hex_nthash); 484 485 if (rc <= 0) 486 return (SMB_PWE_WRITE_FAILED); 487 488 return (SMB_PWE_SUCCESS); 489 } 490 491 static int 492 smb_pwd_lock(void) 493 { 494 int res; 495 496 if (smb_pwd_flck()) { 497 switch (errno) { 498 case EINTR: 499 res = SMB_PWE_BUSY; 500 break; 501 case EACCES: 502 res = SMB_PWE_DENIED; 503 break; 504 case 0: 505 res = SMB_PWE_SUCCESS; 506 break; 507 } 508 } else 509 res = SMB_PWE_SUCCESS; 510 511 return (res); 512 } 513 514 static int 515 smb_pwd_unlock(void) 516 { 517 if (smb_pwd_fulck()) 518 return (SMB_PWE_SYSTEM_ERROR); 519 520 return (SMB_PWE_SUCCESS); 521 } 522 523 static int 524 smb_pwd_flck(void) 525 { 526 int seconds = 0; 527 528 (void) mutex_lock(&lck_lock); 529 for (;;) { 530 if (lck_pid != 0 && lck_pid != getpid()) { 531 /* somebody forked */ 532 lck_pid = 0; 533 lck_tid = 0; 534 } 535 536 if (lck_tid == 0) { 537 if ((fildes = creat(SMB_PASSLCK, 0600)) == -1) 538 break; 539 flock.l_type = F_WRLCK; 540 if (fcntl(fildes, F_SETLK, &flock) != -1) { 541 lck_pid = getpid(); 542 lck_tid = thr_self(); 543 (void) mutex_unlock(&lck_lock); 544 return (0); 545 } 546 (void) close(fildes); 547 fildes = -1; 548 } 549 550 if (seconds++ >= S_WAITTIME) { 551 /* 552 * For compatibility with the past, pretend 553 * that we were interrupted by SIGALRM. 554 */ 555 errno = EINTR; 556 break; 557 } 558 559 (void) mutex_unlock(&lck_lock); 560 (void) sleep(1); 561 (void) mutex_lock(&lck_lock); 562 } 563 (void) mutex_unlock(&lck_lock); 564 565 return (-1); 566 } 567 568 static int 569 smb_pwd_fulck(void) 570 { 571 (void) mutex_lock(&lck_lock); 572 if (lck_tid == thr_self() && fildes >= 0) { 573 flock.l_type = F_UNLCK; 574 (void) fcntl(fildes, F_SETLK, &flock); 575 (void) close(fildes); 576 fildes = -1; 577 lck_pid = 0; 578 lck_tid = 0; 579 (void) mutex_unlock(&lck_lock); 580 return (0); 581 } 582 (void) mutex_unlock(&lck_lock); 583 return (-1); 584 } 585