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