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 <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 int64_t lm_level; 177 178 err = smb_pwd_lock(); 179 if (err != SMB_PWE_SUCCESS) 180 return (err); 181 182 if (stat64(SMB_PASSWD, &stbuf) < 0) { 183 err = SMB_PWE_STAT_FAILED; 184 goto passwd_exit; 185 } 186 187 if ((tempfd = open(SMB_PASSTEMP, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) { 188 err = SMB_PWE_OPEN_FAILED; 189 goto passwd_exit; 190 } 191 192 if ((dst = fdopen(tempfd, "wF")) == NULL) { 193 err = SMB_PWE_OPEN_FAILED; 194 goto passwd_exit; 195 } 196 197 if ((src = fopen(SMB_PASSWD, "rF")) == NULL) { 198 err = SMB_PWE_OPEN_FAILED; 199 (void) fclose(dst); 200 (void) unlink(SMB_PASSTEMP); 201 goto passwd_exit; 202 } 203 204 if (smb_config_getnum(SMB_CI_LM_LEVEL, &lm_level) != SMBD_SMF_OK) 205 lm_level = 4; 206 207 if (lm_level >= 4) 208 control |= SMB_PWC_NOLM; 209 210 /* 211 * copy old password entries to temporary file while replacing 212 * the entry that matches "name" 213 */ 214 pwbuf.pw_name = NULL; 215 pwbuf.pw_pwd = &smbpw; 216 217 while (smb_pwd_fgetent(src, &pwbuf, buf, sizeof (buf)) != NULL) { 218 if (strcmp(pwbuf.pw_name, name) == 0) { 219 err = smb_pwd_chgpwent(&smbpw, password, control); 220 if (err == SMB_PWE_USER_DISABLE) 221 user_disable = B_TRUE; 222 err = smb_pwd_fputent(dst, &pwbuf); 223 newent = B_FALSE; 224 } else { 225 err = smb_pwd_fputent(dst, &pwbuf); 226 } 227 228 if (err != SMB_PWE_SUCCESS) { 229 (void) fclose(src); 230 (void) fclose(dst); 231 goto passwd_exit; 232 } 233 } 234 235 if (newent) { 236 if (getpwnam_r(name, &uxpw, uxbuf, sizeof (uxbuf))) { 237 pwbuf.pw_name = uxpw.pw_name; 238 smbpw.pw_flags = 0; 239 smbpw.pw_uid = uxpw.pw_uid; 240 (void) smb_pwd_chgpwent(&smbpw, password, control); 241 err = smb_pwd_fputent(dst, &pwbuf); 242 } else { 243 err = SMB_PWE_USER_UNKNOWN; 244 } 245 246 if (err != SMB_PWE_SUCCESS) { 247 (void) fclose(src); 248 (void) fclose(dst); 249 goto passwd_exit; 250 } 251 } 252 253 (void) fclose(src); 254 if (fclose(dst) != 0) { 255 err = SMB_PWE_CLOSE_FAILED; 256 goto passwd_exit; /* Don't trust the temporary file */ 257 } 258 259 /* Rename temp to passwd */ 260 if (unlink(SMB_OPASSWD) && access(SMB_OPASSWD, 0) == 0) { 261 err = SMB_PWE_UPDATE_FAILED; 262 (void) unlink(SMB_PASSTEMP); 263 goto passwd_exit; 264 } 265 266 if (link(SMB_PASSWD, SMB_OPASSWD) == -1) { 267 err = SMB_PWE_UPDATE_FAILED; 268 (void) unlink(SMB_PASSTEMP); 269 goto passwd_exit; 270 } 271 272 if (rename(SMB_PASSTEMP, SMB_PASSWD) == -1) { 273 err = SMB_PWE_UPDATE_FAILED; 274 (void) unlink(SMB_PASSTEMP); 275 goto passwd_exit; 276 } 277 278 (void) chmod(SMB_PASSWD, 0400); 279 280 passwd_exit: 281 (void) smb_pwd_unlock(); 282 if ((err == SMB_PWE_SUCCESS) && user_disable) 283 err = SMB_PWE_USER_DISABLE; 284 285 return (err); 286 } 287 288 /* 289 * smb_getpwent 290 * 291 * Parse the buffer in the passed pwbuf and fill in the 292 * smb password structure to point to the parsed information. 293 * The entry format is: 294 * 295 * <user-name>:<user-id>:<LM hash>:<NTLM hash> 296 * 297 * Returns a pointer to the password structure on success, 298 * otherwise returns NULL. 299 */ 300 static smb_pwbuf_t * 301 smb_pwd_fgetent(FILE *fp, smb_pwbuf_t *pwbuf, char *buf, size_t bufsize) 302 { 303 char *argv[SMB_PWD_NARG]; 304 smb_passwd_t *pw; 305 smb_pwdarg_t i; 306 int lm_len, nt_len; 307 308 if (fgets(buf, bufsize, fp) == NULL) 309 return (NULL); 310 (void) trim_whitespace(buf); 311 312 for (i = 0; i < SMB_PWD_NARG; ++i) { 313 if ((argv[i] = strsep((char **)&buf, ":")) == 0) { 314 return (NULL); 315 } 316 } 317 318 if ((*argv[SMB_PWD_NAME] == '\0') || (*argv[SMB_PWD_UID] == '\0')) 319 return (NULL); 320 321 pwbuf->pw_name = argv[SMB_PWD_NAME]; 322 pw = pwbuf->pw_pwd; 323 bzero(pw, sizeof (smb_passwd_t)); 324 pw->pw_uid = strtoul(argv[SMB_PWD_UID], 0, 10); 325 326 if (strcmp(argv[SMB_PWD_LMHASH], SMB_PWD_DISABLE) == 0) { 327 pw->pw_flags |= SMB_PWF_DISABLE; 328 (void) strcpy((char *)pw->pw_lmhash, SMB_PWD_DISABLE); 329 (void) strcpy((char *)pw->pw_nthash, SMB_PWD_DISABLE); 330 return (pwbuf); 331 } 332 333 lm_len = strlen(argv[SMB_PWD_LMHASH]); 334 if (lm_len == SMBAUTH_HEXHASH_SZ) { 335 (void) hextobin(argv[SMB_PWD_LMHASH], SMBAUTH_HEXHASH_SZ, 336 (char *)pw->pw_lmhash, SMBAUTH_HASH_SZ); 337 338 pw->pw_flags |= SMB_PWF_LM; 339 } else if (lm_len != 0) { 340 return (NULL); 341 } 342 343 nt_len = strlen(argv[SMB_PWD_NTHASH]); 344 if (nt_len == SMBAUTH_HEXHASH_SZ) { 345 (void) hextobin(argv[SMB_PWD_NTHASH], SMBAUTH_HEXHASH_SZ, 346 (char *)pw->pw_nthash, SMBAUTH_HASH_SZ); 347 348 pw->pw_flags |= SMB_PWF_NT; 349 } else if (nt_len != 0) { 350 return (NULL); 351 } 352 353 return (pwbuf); 354 } 355 356 static int 357 smb_pwd_chgpwent(smb_passwd_t *smbpw, const char *password, int control) 358 { 359 if (control & SMB_PWC_DISABLE) { 360 smbpw->pw_flags |= SMB_PWF_DISABLE; 361 (void) strcpy((char *)smbpw->pw_lmhash, SMB_PWD_DISABLE); 362 (void) strcpy((char *)smbpw->pw_nthash, SMB_PWD_DISABLE); 363 smbpw->pw_flags &= ~(SMB_PWF_LM | SMB_PWF_NT); 364 return (SMB_PWE_SUCCESS); 365 } else if ((control & SMB_PWC_ENABLE) && 366 (smbpw->pw_flags & SMB_PWF_DISABLE)) { 367 *smbpw->pw_lmhash = '\0'; 368 *smbpw->pw_nthash = '\0'; 369 smbpw->pw_flags &= ~(SMB_PWF_LM | SMB_PWF_NT); 370 return (SMB_PWE_SUCCESS); 371 } 372 373 /* No password update if account is disabled */ 374 if (smbpw->pw_flags & SMB_PWF_DISABLE) 375 return (SMB_PWE_USER_DISABLE); 376 377 if (control & SMB_PWC_NOLM) { 378 smbpw->pw_flags &= ~SMB_PWF_LM; 379 *smbpw->pw_lmhash = '\0'; 380 } else { 381 smbpw->pw_flags |= SMB_PWF_LM; 382 (void) smb_auth_lm_hash((char *)password, smbpw->pw_lmhash); 383 } 384 385 smbpw->pw_flags |= SMB_PWF_NT; 386 (void) smb_auth_ntlm_hash((char *)password, smbpw->pw_nthash); 387 return (SMB_PWE_SUCCESS); 388 } 389 390 /* 391 * smb_putpwent 392 * 393 * Creates LM and NTLM hash from the given plain text password 394 * and write them along with user's name and Id to the smbpasswd 395 * file. 396 */ 397 static int 398 smb_pwd_fputent(FILE *fp, smb_pwbuf_t *pwbuf) 399 { 400 smb_passwd_t *pw = pwbuf->pw_pwd; 401 char hex_nthash[SMBAUTH_HEXHASH_SZ+1]; 402 char hex_lmhash[SMBAUTH_HEXHASH_SZ+1]; 403 int rc; 404 405 if ((pw->pw_flags & SMB_PWF_LM) == SMB_PWF_LM) { 406 (void) bintohex((char *)pw->pw_lmhash, SMBAUTH_HASH_SZ, 407 hex_lmhash, SMBAUTH_HEXHASH_SZ); 408 hex_lmhash[SMBAUTH_HEXHASH_SZ] = '\0'; 409 } else { 410 (void) strcpy(hex_lmhash, (char *)pw->pw_lmhash); 411 } 412 413 if ((pw->pw_flags & SMB_PWF_NT) == SMB_PWF_NT) { 414 (void) bintohex((char *)pw->pw_nthash, SMBAUTH_HASH_SZ, 415 hex_nthash, SMBAUTH_HEXHASH_SZ); 416 hex_nthash[SMBAUTH_HEXHASH_SZ] = '\0'; 417 } else { 418 (void) strcpy(hex_nthash, (char *)pw->pw_nthash); 419 } 420 421 rc = fprintf(fp, "%s:%d:%s:%s\n", pwbuf->pw_name, pw->pw_uid, 422 hex_lmhash, hex_nthash); 423 424 if (rc <= 0) 425 return (SMB_PWE_WRITE_FAILED); 426 427 return (SMB_PWE_SUCCESS); 428 } 429 430 static int 431 smb_pwd_lock(void) 432 { 433 int res; 434 435 if (smb_pwd_flck()) { 436 switch (errno) { 437 case EINTR: 438 res = SMB_PWE_BUSY; 439 break; 440 case EACCES: 441 res = SMB_PWE_DENIED; 442 break; 443 case 0: 444 res = SMB_PWE_SUCCESS; 445 break; 446 } 447 } else 448 res = SMB_PWE_SUCCESS; 449 450 return (res); 451 } 452 453 static int 454 smb_pwd_unlock(void) 455 { 456 if (smb_pwd_fulck()) 457 return (SMB_PWE_SYSTEM_ERROR); 458 459 return (SMB_PWE_SUCCESS); 460 } 461 462 static int 463 smb_pwd_flck(void) 464 { 465 int seconds = 0; 466 467 (void) mutex_lock(&lck_lock); 468 for (;;) { 469 if (lck_pid != 0 && lck_pid != getpid()) { 470 /* somebody forked */ 471 lck_pid = 0; 472 lck_tid = 0; 473 } 474 475 if (lck_tid == 0) { 476 if ((fildes = creat(SMB_PASSLCK, 0600)) == -1) 477 break; 478 flock.l_type = F_WRLCK; 479 if (fcntl(fildes, F_SETLK, &flock) != -1) { 480 lck_pid = getpid(); 481 lck_tid = thr_self(); 482 (void) mutex_unlock(&lck_lock); 483 return (0); 484 } 485 (void) close(fildes); 486 fildes = -1; 487 } 488 489 if (seconds++ >= S_WAITTIME) { 490 /* 491 * For compatibility with the past, pretend 492 * that we were interrupted by SIGALRM. 493 */ 494 errno = EINTR; 495 break; 496 } 497 498 (void) mutex_unlock(&lck_lock); 499 (void) sleep(1); 500 (void) mutex_lock(&lck_lock); 501 } 502 (void) mutex_unlock(&lck_lock); 503 504 return (-1); 505 } 506 507 static int 508 smb_pwd_fulck(void) 509 { 510 (void) mutex_lock(&lck_lock); 511 if (lck_tid == thr_self() && fildes >= 0) { 512 flock.l_type = F_UNLCK; 513 (void) fcntl(fildes, F_SETLK, &flock); 514 (void) close(fildes); 515 fildes = -1; 516 lck_pid = 0; 517 lck_tid = 0; 518 (void) mutex_unlock(&lck_lock); 519 return (0); 520 } 521 (void) mutex_unlock(&lck_lock); 522 return (-1); 523 } 524