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