1/*
2 * Unix SMB/CIFS implementation.
3 * SMB parameters and setup
4 * Copyright (C) Andrew Tridgell       1992-1998
5 * Modified by Jeremy Allison          1995.
6 * Modified by Gerald (Jerry) Carter   2000-2001,2003
7 * Modified by Andrew Bartlett         2002.
8 *
9 * This program is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License as published by the Free
11 * Software Foundation; either version 2 of the License, or (at your option)
12 * any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
17 * more details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * this program; if not, write to the Free Software Foundation, Inc., 675
21 * Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24#include "includes.h"
25
26#undef DBGC_CLASS
27#define DBGC_CLASS DBGC_PASSDB
28
29/*
30   smb_passwd is analogous to sam_passwd used everywhere
31   else.  However, smb_passwd is limited to the information
32   stored by an smbpasswd entry
33 */
34
35struct smb_passwd
36{
37        uint32 smb_userid;        /* this is actually the unix uid_t */
38        const char *smb_name;     /* username string */
39
40        const unsigned char *smb_passwd;    /* Null if no password */
41        const unsigned char *smb_nt_passwd; /* Null if no password */
42
43        uint16 acct_ctrl;             /* account info (ACB_xxxx bit-mask) */
44        time_t pass_last_set_time;    /* password last set time */
45};
46
47struct smbpasswd_privates
48{
49	/* used for maintain locks on the smbpasswd file */
50	int 	pw_file_lock_depth;
51
52	/* Global File pointer */
53	FILE 	*pw_file;
54
55	/* formerly static variables */
56	struct smb_passwd pw_buf;
57	pstring  user_name;
58	unsigned char smbpwd[16];
59	unsigned char smbntpwd[16];
60
61	/* retrive-once info */
62	const char *smbpasswd_file;
63};
64
65enum pwf_access_type { PWF_READ, PWF_UPDATE, PWF_CREATE };
66
67/***************************************************************
68 Lock an fd. Abandon after waitsecs seconds.
69****************************************************************/
70
71static BOOL pw_file_lock(int fd, int type, int secs, int *plock_depth)
72{
73	if (fd < 0) {
74		return False;
75	}
76
77	if(*plock_depth == 0) {
78		if (!do_file_lock(fd, secs, type)) {
79			DEBUG(10,("pw_file_lock: locking file failed, error = %s.\n",
80				strerror(errno)));
81			return False;
82		}
83	}
84
85	(*plock_depth)++;
86
87	return True;
88}
89
90/***************************************************************
91 Unlock an fd. Abandon after waitsecs seconds.
92****************************************************************/
93
94static BOOL pw_file_unlock(int fd, int *plock_depth)
95{
96	BOOL ret=True;
97
98	if (fd == 0 || *plock_depth == 0) {
99		return True;
100	}
101
102	if(*plock_depth == 1) {
103		ret = do_file_lock(fd, 5, F_UNLCK);
104	}
105
106	if (*plock_depth > 0) {
107		(*plock_depth)--;
108	}
109
110	if(!ret) {
111		DEBUG(10,("pw_file_unlock: unlocking file failed, error = %s.\n",
112			strerror(errno)));
113	}
114	return ret;
115}
116
117/**************************************************************
118 Intialize a smb_passwd struct
119 *************************************************************/
120
121static void pdb_init_smb(struct smb_passwd *user)
122{
123	if (user == NULL)
124		return;
125	ZERO_STRUCTP (user);
126
127	user->pass_last_set_time = (time_t)0;
128}
129
130/***************************************************************
131 Internal fn to enumerate the smbpasswd list. Returns a void pointer
132 to ensure no modification outside this module. Checks for atomic
133 rename of smbpasswd file on update or create once the lock has
134 been granted to prevent race conditions. JRA.
135****************************************************************/
136
137static FILE *startsmbfilepwent(const char *pfile, enum pwf_access_type type, int *lock_depth)
138{
139	FILE *fp = NULL;
140	const char *open_mode = NULL;
141	int race_loop = 0;
142	int lock_type = F_RDLCK;
143
144	if (!*pfile) {
145		DEBUG(0, ("startsmbfilepwent: No SMB password file set\n"));
146		return (NULL);
147	}
148
149	switch(type) {
150		case PWF_READ:
151			open_mode = "rb";
152			lock_type = F_RDLCK;
153			break;
154		case PWF_UPDATE:
155			open_mode = "r+b";
156			lock_type = F_WRLCK;
157			break;
158		case PWF_CREATE:
159			/*
160			 * Ensure atomic file creation.
161			 */
162			{
163				int i, fd = -1;
164
165				for(i = 0; i < 5; i++) {
166					if((fd = sys_open(pfile, O_CREAT|O_TRUNC|O_EXCL|O_RDWR, 0600))!=-1) {
167						break;
168					}
169					sys_usleep(200); /* Spin, spin... */
170				}
171				if(fd == -1) {
172					DEBUG(0,("startsmbfilepwent_internal: too many race conditions \
173creating file %s\n", pfile));
174					return NULL;
175				}
176				close(fd);
177				open_mode = "r+b";
178				lock_type = F_WRLCK;
179				break;
180			}
181	}
182
183	for(race_loop = 0; race_loop < 5; race_loop++) {
184		DEBUG(10, ("startsmbfilepwent_internal: opening file %s\n", pfile));
185
186		if((fp = sys_fopen(pfile, open_mode)) == NULL) {
187
188			/*
189			 * If smbpasswd file doesn't exist, then create new one. This helps to avoid
190			 * confusing error msg when adding user account first time.
191			 */
192			if (errno == ENOENT) {
193				if ((fp = sys_fopen(pfile, "a+")) != NULL) {
194					DEBUG(0, ("startsmbfilepwent_internal: file %s did not \
195exist. File successfully created.\n", pfile));
196				} else {
197					DEBUG(0, ("startsmbfilepwent_internal: file %s did not \
198exist. Couldn't create new one. Error was: %s",
199					pfile, strerror(errno)));
200					return NULL;
201				}
202			} else {
203				DEBUG(0, ("startsmbfilepwent_internal: unable to open file %s. \
204Error was: %s\n", pfile, strerror(errno)));
205				return NULL;
206			}
207		}
208
209		if (!pw_file_lock(fileno(fp), lock_type, 5, lock_depth)) {
210			DEBUG(0, ("startsmbfilepwent_internal: unable to lock file %s. \
211Error was %s\n", pfile, strerror(errno) ));
212			fclose(fp);
213			return NULL;
214		}
215
216		/*
217		 * Only check for replacement races on update or create.
218		 * For read we don't mind if the data is one record out of date.
219		 */
220
221		if(type == PWF_READ) {
222			break;
223		} else {
224			SMB_STRUCT_STAT sbuf1, sbuf2;
225
226			/*
227			 * Avoid the potential race condition between the open and the lock
228			 * by doing a stat on the filename and an fstat on the fd. If the
229			 * two inodes differ then someone did a rename between the open and
230			 * the lock. Back off and try the open again. Only do this 5 times to
231			 * prevent infinate loops. JRA.
232			 */
233
234			if (sys_stat(pfile,&sbuf1) != 0) {
235				DEBUG(0, ("startsmbfilepwent_internal: unable to stat file %s. \
236Error was %s\n", pfile, strerror(errno)));
237				pw_file_unlock(fileno(fp), lock_depth);
238				fclose(fp);
239				return NULL;
240			}
241
242			if (sys_fstat(fileno(fp),&sbuf2) != 0) {
243				DEBUG(0, ("startsmbfilepwent_internal: unable to fstat file %s. \
244Error was %s\n", pfile, strerror(errno)));
245				pw_file_unlock(fileno(fp), lock_depth);
246				fclose(fp);
247				return NULL;
248			}
249
250			if( sbuf1.st_ino == sbuf2.st_ino) {
251				/* No race. */
252				break;
253			}
254
255			/*
256			 * Race occurred - back off and try again...
257			 */
258
259			pw_file_unlock(fileno(fp), lock_depth);
260			fclose(fp);
261		}
262	}
263
264	if(race_loop == 5) {
265		DEBUG(0, ("startsmbfilepwent_internal: too many race conditions opening file %s\n", pfile));
266		return NULL;
267	}
268
269	/* Set a buffer to do more efficient reads */
270	setvbuf(fp, (char *)NULL, _IOFBF, 1024);
271
272	/* Make sure it is only rw by the owner */
273#ifdef HAVE_FCHMOD
274	if(fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) {
275#else
276	if(chmod(pfile, S_IRUSR|S_IWUSR) == -1) {
277#endif
278		DEBUG(0, ("startsmbfilepwent_internal: failed to set 0600 permissions on password file %s. \
279Error was %s\n.", pfile, strerror(errno) ));
280		pw_file_unlock(fileno(fp), lock_depth);
281		fclose(fp);
282		return NULL;
283	}
284
285	/* We have a lock on the file. */
286	return fp;
287}
288
289/***************************************************************
290 End enumeration of the smbpasswd list.
291****************************************************************/
292
293static void endsmbfilepwent(FILE *fp, int *lock_depth)
294{
295	if (!fp) {
296		return;
297	}
298
299	pw_file_unlock(fileno(fp), lock_depth);
300	fclose(fp);
301	DEBUG(7, ("endsmbfilepwent_internal: closed password file.\n"));
302}
303
304/*************************************************************************
305 Routine to return the next entry in the smbpasswd list.
306 *************************************************************************/
307
308static struct smb_passwd *getsmbfilepwent(struct smbpasswd_privates *smbpasswd_state, FILE *fp)
309{
310	/* Static buffers we will return. */
311	struct smb_passwd *pw_buf = &smbpasswd_state->pw_buf;
312	char  *user_name = smbpasswd_state->user_name;
313	unsigned char *smbpwd = smbpasswd_state->smbpwd;
314	unsigned char *smbntpwd = smbpasswd_state->smbntpwd;
315	char linebuf[256];
316	unsigned char c;
317	unsigned char *p;
318	long uidval;
319	size_t linebuf_len;
320
321	if(fp == NULL) {
322		DEBUG(0,("getsmbfilepwent: Bad password file pointer.\n"));
323		return NULL;
324	}
325
326	pdb_init_smb(pw_buf);
327	pw_buf->acct_ctrl = ACB_NORMAL;
328
329	/*
330	 * Scan the file, a line at a time and check if the name matches.
331	 */
332	while (!feof(fp)) {
333		linebuf[0] = '\0';
334
335		fgets(linebuf, 256, fp);
336		if (ferror(fp)) {
337			return NULL;
338		}
339
340		/*
341		 * Check if the string is terminated with a newline - if not
342		 * then we must keep reading and discard until we get one.
343		 */
344		if ((linebuf_len = strlen(linebuf)) == 0) {
345			continue;
346		}
347
348		if (linebuf[linebuf_len - 1] != '\n') {
349			c = '\0';
350			while (!ferror(fp) && !feof(fp)) {
351				c = fgetc(fp);
352				if (c == '\n') {
353					break;
354				}
355			}
356		} else {
357			linebuf[linebuf_len - 1] = '\0';
358		}
359
360#ifdef DEBUG_PASSWORD
361		DEBUG(100, ("getsmbfilepwent: got line |%s|\n", linebuf));
362#endif
363		if ((linebuf[0] == 0) && feof(fp)) {
364			DEBUG(4, ("getsmbfilepwent: end of file reached\n"));
365			break;
366		}
367
368		/*
369		 * The line we have should be of the form :-
370		 *
371		 * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently
372		 * ignored....
373		 *
374		 * or,
375		 *
376		 * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored....
377		 *
378		 * if Windows NT compatible passwords are also present.
379		 * [Account type] is an ascii encoding of the type of account.
380		 * LCT-(8 hex digits) is the time_t value of the last change time.
381		 */
382
383		if (linebuf[0] == '#' || linebuf[0] == '\0') {
384			DEBUG(6, ("getsmbfilepwent: skipping comment or blank line\n"));
385			continue;
386		}
387		p = (unsigned char *) strchr_m(linebuf, ':');
388		if (p == NULL) {
389			DEBUG(0, ("getsmbfilepwent: malformed password entry (no :)\n"));
390			continue;
391		}
392
393		/*
394		 * As 256 is shorter than a pstring we don't need to check
395		 * length here - if this ever changes....
396		 */
397		SMB_ASSERT(sizeof(pstring) > sizeof(linebuf));
398
399		strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
400		user_name[PTR_DIFF(p, linebuf)] = '\0';
401
402		/* Get smb uid. */
403
404		p++; /* Go past ':' */
405
406		if(*p == '-') {
407			DEBUG(0, ("getsmbfilepwent: user name %s has a negative uid.\n", user_name));
408			continue;
409		}
410
411		if (!isdigit(*p)) {
412			DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (uid not number)\n",
413				user_name));
414			continue;
415		}
416
417		uidval = atoi((char *) p);
418
419		while (*p && isdigit(*p)) {
420			p++;
421		}
422
423		if (*p != ':') {
424			DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (no : after uid)\n",
425				user_name));
426			continue;
427		}
428
429		pw_buf->smb_name = user_name;
430		pw_buf->smb_userid = uidval;
431
432		/*
433		 * Now get the password value - this should be 32 hex digits
434		 * which are the ascii representations of a 16 byte string.
435		 * Get two at a time and put them into the password.
436		 */
437
438		/* Skip the ':' */
439		p++;
440
441		if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
442			DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (passwd too short)\n",
443				user_name ));
444			continue;
445		}
446
447		if (p[32] != ':') {
448			DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (no terminating :)\n",
449				user_name));
450			continue;
451		}
452
453		if (strnequal((char *) p, "NO PASSWORD", 11)) {
454			pw_buf->smb_passwd = NULL;
455			pw_buf->acct_ctrl |= ACB_PWNOTREQ;
456		} else {
457			if (*p == '*' || *p == 'X') {
458				/* NULL LM password */
459				pw_buf->smb_passwd = NULL;
460				DEBUG(10, ("getsmbfilepwent: LM password for user %s invalidated\n", user_name));
461			} else if (pdb_gethexpwd((char *)p, smbpwd)) {
462				pw_buf->smb_passwd = smbpwd;
463			} else {
464				pw_buf->smb_passwd = NULL;
465				DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry for user %s \
466(non hex chars)\n", user_name));
467			}
468		}
469
470		/*
471		 * Now check if the NT compatible password is
472		 * available.
473		 */
474		pw_buf->smb_nt_passwd = NULL;
475		p += 33; /* Move to the first character of the line after the lanman password. */
476		if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
477			if (*p != '*' && *p != 'X') {
478				if(pdb_gethexpwd((char *)p,smbntpwd)) {
479					pw_buf->smb_nt_passwd = smbntpwd;
480				}
481			}
482			p += 33; /* Move to the first character of the line after the NT password. */
483		}
484
485		DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n",
486			user_name, uidval));
487
488		if (*p == '[') {
489			unsigned char *end_p = (unsigned char *)strchr_m((char *)p, ']');
490			pw_buf->acct_ctrl = pdb_decode_acct_ctrl((char*)p);
491
492			/* Must have some account type set. */
493			if(pw_buf->acct_ctrl == 0) {
494				pw_buf->acct_ctrl = ACB_NORMAL;
495			}
496
497			/* Now try and get the last change time. */
498			if(end_p) {
499				p = end_p + 1;
500			}
501			if(*p == ':') {
502				p++;
503				if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) {
504					int i;
505					p += 4;
506					for(i = 0; i < 8; i++) {
507						if(p[i] == '\0' || !isxdigit(p[i])) {
508							break;
509						}
510					}
511					if(i == 8) {
512						/*
513						 * p points at 8 characters of hex digits -
514						 * read into a time_t as the seconds since
515						 * 1970 that the password was last changed.
516						 */
517						pw_buf->pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
518					}
519				}
520			}
521		} else {
522			/* 'Old' style file. Fake up based on user name. */
523			/*
524			 * Currently trust accounts are kept in the same
525			 * password file as 'normal accounts'. If this changes
526			 * we will have to fix this code. JRA.
527			 */
528			if(pw_buf->smb_name[strlen(pw_buf->smb_name) - 1] == '$') {
529				pw_buf->acct_ctrl &= ~ACB_NORMAL;
530				pw_buf->acct_ctrl |= ACB_WSTRUST;
531			}
532		}
533
534		return pw_buf;
535	}
536
537	DEBUG(5,("getsmbfilepwent: end of file reached.\n"));
538	return NULL;
539}
540
541/************************************************************************
542 Create a new smbpasswd entry - malloced space returned.
543*************************************************************************/
544
545static char *format_new_smbpasswd_entry(const struct smb_passwd *newpwd)
546{
547	int new_entry_length;
548	char *new_entry;
549	char *p;
550
551	new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 +
552				NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2;
553
554	if((new_entry = (char *)SMB_MALLOC( new_entry_length )) == NULL) {
555		DEBUG(0, ("format_new_smbpasswd_entry: Malloc failed adding entry for user %s.\n",
556			newpwd->smb_name ));
557		return NULL;
558	}
559
560	slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid);
561
562	p = new_entry+strlen(new_entry);
563	pdb_sethexpwd(p, newpwd->smb_passwd, newpwd->acct_ctrl);
564	p+=strlen(p);
565	*p = ':';
566	p++;
567
568	pdb_sethexpwd(p, newpwd->smb_nt_passwd, newpwd->acct_ctrl);
569	p+=strlen(p);
570	*p = ':';
571	p++;
572
573	/* Add the account encoding and the last change time. */
574	slprintf((char *)p, new_entry_length - 1 - (p - new_entry),  "%s:LCT-%08X:\n",
575		pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN),
576		(uint32)newpwd->pass_last_set_time);
577
578	return new_entry;
579}
580
581/************************************************************************
582 Routine to add an entry to the smbpasswd file.
583*************************************************************************/
584
585static BOOL add_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, struct smb_passwd *newpwd)
586{
587	const char *pfile = smbpasswd_state->smbpasswd_file;
588	struct smb_passwd *pwd = NULL;
589	FILE *fp = NULL;
590	int wr_len;
591	int fd;
592	size_t new_entry_length;
593	char *new_entry;
594	SMB_OFF_T offpos;
595	uint32 max_found_uid = 0;
596
597	/* Open the smbpassword file - for update. */
598	fp = startsmbfilepwent(pfile, PWF_UPDATE, &smbpasswd_state->pw_file_lock_depth);
599
600	if (fp == NULL && errno == ENOENT) {
601		/* Try again - create. */
602		fp = startsmbfilepwent(pfile, PWF_CREATE, &smbpasswd_state->pw_file_lock_depth);
603	}
604
605	if (fp == NULL) {
606		DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n"));
607		return False;
608	}
609
610	/*
611	 * Scan the file, a line at a time and check if the name matches.
612	 */
613
614	while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
615		if (strequal(newpwd->smb_name, pwd->smb_name)) {
616			DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name));
617			endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
618			return False;
619		}
620
621		/* Look for a free uid for use in non-unix accounts */
622		if (pwd->smb_userid > max_found_uid) {
623			max_found_uid = pwd->smb_userid;
624		}
625	}
626
627	/* Ok - entry doesn't exist. We can add it */
628
629	/* Create a new smb passwd entry and set it to the given password. */
630	/*
631	 * The add user write needs to be atomic - so get the fd from
632	 * the fp and do a raw write() call.
633	 */
634	fd = fileno(fp);
635
636	if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1) {
637		DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \
638Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
639		endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
640		return False;
641	}
642
643	if((new_entry = format_new_smbpasswd_entry(newpwd)) == NULL) {
644		DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
645Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
646		endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
647		return False;
648	}
649
650	new_entry_length = strlen(new_entry);
651
652#ifdef DEBUG_PASSWORD
653	DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d made line |%s|",
654			fd, new_entry_length, new_entry));
655#endif
656
657	if ((wr_len = write(fd, new_entry, new_entry_length)) != new_entry_length) {
658		DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \
659Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
660
661		/* Remove the entry we just wrote. */
662		if(sys_ftruncate(fd, offpos) == -1) {
663			DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \
664Error was %s. Password file may be corrupt ! Please examine by hand !\n",
665				newpwd->smb_name, strerror(errno)));
666		}
667
668		endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
669		free(new_entry);
670		return False;
671	}
672
673	free(new_entry);
674	endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
675	return True;
676}
677
678/************************************************************************
679 Routine to search the smbpasswd file for an entry matching the username.
680 and then modify its password entry. We can't use the startsmbpwent()/
681 getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
682 in the actual file to decide how much room we have to write data.
683 override = False, normal
684 override = True, override XXXXXXXX'd out password or NO PASS
685************************************************************************/
686
687static BOOL mod_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const struct smb_passwd* pwd)
688{
689	/* Static buffers we will return. */
690	pstring user_name;
691
692	char linebuf[256];
693	char readbuf[1024];
694	unsigned char c;
695	fstring ascii_p16;
696	fstring encode_bits;
697	unsigned char *p = NULL;
698	size_t linebuf_len = 0;
699	FILE *fp;
700	int lockfd;
701	const char *pfile = smbpasswd_state->smbpasswd_file;
702	BOOL found_entry = False;
703	BOOL got_pass_last_set_time = False;
704
705	SMB_OFF_T pwd_seekpos = 0;
706
707	int i;
708	int wr_len;
709	int fd;
710
711	if (!*pfile) {
712		DEBUG(0, ("No SMB password file set\n"));
713		return False;
714	}
715	DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
716
717	fp = sys_fopen(pfile, "r+");
718
719	if (fp == NULL) {
720		DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
721		return False;
722	}
723	/* Set a buffer to do more efficient reads */
724	setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
725
726	lockfd = fileno(fp);
727
728	if (!pw_file_lock(lockfd, F_WRLCK, 5, &smbpasswd_state->pw_file_lock_depth)) {
729		DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile));
730		fclose(fp);
731		return False;
732	}
733
734	/* Make sure it is only rw by the owner */
735	chmod(pfile, 0600);
736
737	/* We have a write lock on the file. */
738	/*
739	 * Scan the file, a line at a time and check if the name matches.
740	 */
741	while (!feof(fp)) {
742		pwd_seekpos = sys_ftell(fp);
743
744		linebuf[0] = '\0';
745
746		fgets(linebuf, sizeof(linebuf), fp);
747		if (ferror(fp)) {
748			pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
749			fclose(fp);
750			return False;
751		}
752
753		/*
754		 * Check if the string is terminated with a newline - if not
755		 * then we must keep reading and discard until we get one.
756		 */
757		linebuf_len = strlen(linebuf);
758		if (linebuf[linebuf_len - 1] != '\n') {
759			c = '\0';
760			while (!ferror(fp) && !feof(fp)) {
761				c = fgetc(fp);
762				if (c == '\n') {
763					break;
764				}
765			}
766		} else {
767			linebuf[linebuf_len - 1] = '\0';
768		}
769
770#ifdef DEBUG_PASSWORD
771		DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
772#endif
773
774		if ((linebuf[0] == 0) && feof(fp)) {
775			DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
776			break;
777		}
778
779		/*
780		 * The line we have should be of the form :-
781		 *
782		 * username:uid:[32hex bytes]:....other flags presently
783		 * ignored....
784		 *
785		 * or,
786		 *
787		 * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
788		 *
789		 * if Windows NT compatible passwords are also present.
790		 */
791
792		if (linebuf[0] == '#' || linebuf[0] == '\0') {
793			DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
794			continue;
795		}
796
797		p = (unsigned char *) strchr_m(linebuf, ':');
798
799		if (p == NULL) {
800			DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
801			continue;
802		}
803
804		/*
805		 * As 256 is shorter than a pstring we don't need to check
806		 * length here - if this ever changes....
807		 */
808
809		SMB_ASSERT(sizeof(user_name) > sizeof(linebuf));
810
811		strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
812		user_name[PTR_DIFF(p, linebuf)] = '\0';
813		if (strequal(user_name, pwd->smb_name)) {
814			found_entry = True;
815			break;
816		}
817	}
818
819	if (!found_entry) {
820		pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
821		fclose(fp);
822
823		DEBUG(2, ("Cannot update entry for user %s, as they don't exist in the smbpasswd file!\n",
824			pwd->smb_name));
825		return False;
826	}
827
828	DEBUG(6, ("mod_smbfilepwd_entry: entry exists for user %s\n", pwd->smb_name));
829
830	/* User name matches - get uid and password */
831	p++; /* Go past ':' */
832
833	if (!isdigit(*p)) {
834		DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (uid not number)\n",
835			pwd->smb_name));
836		pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
837		fclose(fp);
838		return False;
839	}
840
841	while (*p && isdigit(*p)) {
842		p++;
843	}
844	if (*p != ':') {
845		DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no : after uid)\n",
846			pwd->smb_name));
847		pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
848		fclose(fp);
849		return False;
850	}
851
852	/*
853	 * Now get the password value - this should be 32 hex digits
854	 * which are the ascii representations of a 16 byte string.
855	 * Get two at a time and put them into the password.
856	 */
857	p++;
858
859	/* Record exact password position */
860	pwd_seekpos += PTR_DIFF(p, linebuf);
861
862	if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
863		DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (passwd too short)\n",
864			pwd->smb_name));
865		pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
866		fclose(fp);
867		return (False);
868	}
869
870	if (p[32] != ':') {
871		DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no terminating :)\n",
872			pwd->smb_name));
873		pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
874		fclose(fp);
875		return False;
876	}
877
878	/* Now check if the NT compatible password is available. */
879	p += 33; /* Move to the first character of the line after the lanman password. */
880	if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
881		DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (passwd too short)\n",
882			pwd->smb_name));
883		pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
884		fclose(fp);
885		return (False);
886	}
887
888	if (p[32] != ':') {
889		DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no terminating :)\n",
890			pwd->smb_name));
891		pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
892		fclose(fp);
893		return False;
894	}
895
896	/*
897	 * Now check if the account info and the password last
898	 * change time is available.
899	 */
900	p += 33; /* Move to the first character of the line after the NT password. */
901
902	if (*p == '[') {
903		i = 0;
904		encode_bits[i++] = *p++;
905		while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']')) {
906			encode_bits[i++] = *p++;
907		}
908
909		encode_bits[i++] = ']';
910		encode_bits[i++] = '\0';
911
912		if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
913			/*
914			 * We are using a new format, space padded
915			 * acct ctrl field. Encode the given acct ctrl
916			 * bits into it.
917			 */
918			fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
919		} else {
920			DEBUG(0,("mod_smbfilepwd_entry:  Using old smbpasswd format for user %s. \
921This is no longer supported.!\n", pwd->smb_name));
922			DEBUG(0,("mod_smbfilepwd_entry:  No changes made, failing.!\n"));
923			pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
924			fclose(fp);
925			return False;
926		}
927
928		/* Go past the ']' */
929		if(linebuf_len > PTR_DIFF(p, linebuf)) {
930			p++;
931		}
932
933		if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
934			p++;
935
936			/* We should be pointing at the LCT entry. */
937			if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
938				p += 4;
939				for(i = 0; i < 8; i++) {
940					if(p[i] == '\0' || !isxdigit(p[i])) {
941						break;
942					}
943				}
944				if(i == 8) {
945					/*
946					 * p points at 8 characters of hex digits -
947					 * read into a time_t as the seconds since
948					 * 1970 that the password was last changed.
949					 */
950					got_pass_last_set_time = True;
951				} /* i == 8 */
952			} /* *p && StrnCaseCmp() */
953		} /* p == ':' */
954	} /* p == '[' */
955
956	/* Entry is correctly formed. */
957
958	/* Create the 32 byte representation of the new p16 */
959	pdb_sethexpwd(ascii_p16, pwd->smb_passwd, pwd->acct_ctrl);
960
961	/* Add on the NT md4 hash */
962	ascii_p16[32] = ':';
963	wr_len = 66;
964	pdb_sethexpwd(ascii_p16+33, pwd->smb_nt_passwd, pwd->acct_ctrl);
965	ascii_p16[65] = ':';
966	ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
967
968	/* Add on the account info bits and the time of last password change. */
969	if(got_pass_last_set_time) {
970		slprintf(&ascii_p16[strlen(ascii_p16)],
971			sizeof(ascii_p16)-(strlen(ascii_p16)+1),
972			"%s:LCT-%08X:",
973			encode_bits, (uint32)pwd->pass_last_set_time );
974		wr_len = strlen(ascii_p16);
975	}
976
977#ifdef DEBUG_PASSWORD
978	DEBUG(100,("mod_smbfilepwd_entry: "));
979	dump_data(100, ascii_p16, wr_len);
980#endif
981
982	if(wr_len > sizeof(linebuf)) {
983		DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
984		pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
985		fclose(fp);
986		return (False);
987	}
988
989	/*
990	 * Do an atomic write into the file at the position defined by
991	 * seekpos.
992	 */
993
994	/* The mod user write needs to be atomic - so get the fd from
995		the fp and do a raw write() call.
996	 */
997
998	fd = fileno(fp);
999
1000	if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
1001		DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1002		pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1003		fclose(fp);
1004		return False;
1005	}
1006
1007	/* Sanity check - ensure the areas we are writing are framed by ':' */
1008	if (read(fd, linebuf, wr_len+1) != wr_len+1) {
1009		DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
1010		pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1011		fclose(fp);
1012		return False;
1013	}
1014
1015	if ((linebuf[0] != ':') || (linebuf[wr_len] != ':'))	{
1016		DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
1017		pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1018		fclose(fp);
1019		return False;
1020	}
1021
1022	if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
1023		DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1024		pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1025		fclose(fp);
1026		return False;
1027	}
1028
1029    /************************************************************************
1030    2007/09/10, zzz save data
1031    *************************************************************************/
1032#if 0
1033    {
1034        char cmd[256];
1035
1036        //uid always 0
1037        snprintf(cmd, 256, "/usr/sbin/nvram set smb_pass=\"%s:0:%s\"", user_name, ascii_p16);
1038        printf("cmd:%s\n", cmd);
1039        system(cmd);
1040        system("/usr/sbin/nvram commit");
1041    }
1042#endif //zzz
1043	if (write(fd, ascii_p16, wr_len) != wr_len) {
1044		DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
1045		pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1046		fclose(fp);
1047		return False;
1048	}
1049
1050	pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1051	fclose(fp);
1052	return True;
1053}
1054
1055/************************************************************************
1056 Routine to delete an entry in the smbpasswd file by name.
1057*************************************************************************/
1058
1059static BOOL del_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const char *name)
1060{
1061	const char *pfile = smbpasswd_state->smbpasswd_file;
1062	pstring pfile2;
1063	struct smb_passwd *pwd = NULL;
1064	FILE *fp = NULL;
1065	FILE *fp_write = NULL;
1066	int pfile2_lockdepth = 0;
1067
1068	slprintf(pfile2, sizeof(pfile2)-1, "%s.%u", pfile, (unsigned)sys_getpid() );
1069
1070	/*
1071	 * Open the smbpassword file - for update. It needs to be update
1072	 * as we need any other processes to wait until we have replaced
1073	 * it.
1074	 */
1075
1076	if((fp = startsmbfilepwent(pfile, PWF_UPDATE, &smbpasswd_state->pw_file_lock_depth)) == NULL) {
1077		DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1078		return False;
1079	}
1080
1081	/*
1082	 * Create the replacement password file.
1083	 */
1084	if((fp_write = startsmbfilepwent(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) {
1085		DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1086		endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1087		return False;
1088	}
1089
1090	/*
1091	 * Scan the file, a line at a time and check if the name matches.
1092	 */
1093
1094	while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
1095		char *new_entry;
1096		size_t new_entry_length;
1097
1098		if (strequal(name, pwd->smb_name)) {
1099			DEBUG(10, ("add_smbfilepwd_entry: found entry with name %s - deleting it.\n", name));
1100			continue;
1101		}
1102
1103		/*
1104		 * We need to copy the entry out into the second file.
1105		 */
1106
1107		if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL) {
1108			DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \
1109Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1110			unlink(pfile2);
1111			endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1112			endsmbfilepwent(fp_write, &pfile2_lockdepth);
1113			return False;
1114		}
1115
1116		new_entry_length = strlen(new_entry);
1117
1118		if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length) {
1119			DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \
1120Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1121			unlink(pfile2);
1122			endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1123			endsmbfilepwent(fp_write, &pfile2_lockdepth);
1124			free(new_entry);
1125			return False;
1126		}
1127
1128		free(new_entry);
1129	}
1130
1131	/*
1132	 * Ensure pfile2 is flushed before rename.
1133	 */
1134
1135	if(fflush(fp_write) != 0) {
1136		DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno)));
1137		endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1138		endsmbfilepwent(fp_write,&pfile2_lockdepth);
1139		return False;
1140	}
1141
1142	/*
1143	 * Do an atomic rename - then release the locks.
1144	 */
1145
1146	if(rename(pfile2,pfile) != 0) {
1147		unlink(pfile2);
1148	}
1149
1150	endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1151	endsmbfilepwent(fp_write,&pfile2_lockdepth);
1152	return True;
1153}
1154
1155/*********************************************************************
1156 Create a smb_passwd struct from a SAM_ACCOUNT.
1157 We will not allocate any new memory.  The smb_passwd struct
1158 should only stay around as long as the SAM_ACCOUNT does.
1159 ********************************************************************/
1160
1161static BOOL build_smb_pass (struct smb_passwd *smb_pw, const SAM_ACCOUNT *sampass)
1162{
1163	uint32 rid;
1164
1165	if (sampass == NULL)
1166		return False;
1167	ZERO_STRUCTP(smb_pw);
1168
1169	if (!IS_SAM_DEFAULT(sampass, PDB_USERSID)) {
1170		rid = pdb_get_user_rid(sampass);
1171
1172		/* If the user specified a RID, make sure its able to be both stored and retreived */
1173		if (rid == DOMAIN_USER_RID_GUEST) {
1174			struct passwd *passwd = getpwnam_alloc(lp_guestaccount());
1175			if (!passwd) {
1176				DEBUG(0, ("Could not find gest account via getpwnam()! (%s)\n", lp_guestaccount()));
1177				return False;
1178			}
1179			smb_pw->smb_userid=passwd->pw_uid;
1180			passwd_free(&passwd);
1181
1182		} else if (algorithmic_pdb_rid_is_user(rid)) {
1183			smb_pw->smb_userid=algorithmic_pdb_user_rid_to_uid(rid);
1184		} else {
1185			DEBUG(0,("build_sam_pass: Failing attempt to store user with non-uid based user RID. \n"));
1186			return False;
1187		}
1188	}
1189
1190	smb_pw->smb_name=(const char*)pdb_get_username(sampass);
1191
1192	smb_pw->smb_passwd=pdb_get_lanman_passwd(sampass);
1193	smb_pw->smb_nt_passwd=pdb_get_nt_passwd(sampass);
1194
1195	smb_pw->acct_ctrl=pdb_get_acct_ctrl(sampass);
1196	smb_pw->pass_last_set_time=pdb_get_pass_last_set_time(sampass);
1197
1198	return True;
1199}
1200
1201/*********************************************************************
1202 Create a SAM_ACCOUNT from a smb_passwd struct
1203 ********************************************************************/
1204
1205static BOOL build_sam_account(struct smbpasswd_privates *smbpasswd_state,
1206			      SAM_ACCOUNT *sam_pass, const struct smb_passwd *pw_buf)
1207{
1208	struct passwd *pwfile;
1209
1210	if (sam_pass==NULL) {
1211		DEBUG(5,("build_sam_account: SAM_ACCOUNT is NULL\n"));
1212		return False;
1213	}
1214
1215	/* verify the user account exists */
1216
1217	if ( !(pwfile = getpwnam_alloc(pw_buf->smb_name)) ) {
1218		DEBUG(0,("build_sam_account: smbpasswd database is corrupt!  username %s with uid "
1219		"%u is not in unix passwd database!\n", pw_buf->smb_name, pw_buf->smb_userid));
1220			return False;
1221	}
1222
1223	if (!NT_STATUS_IS_OK(pdb_fill_sam_pw(sam_pass, pwfile)))
1224		return False;
1225
1226	passwd_free(&pwfile);
1227
1228	/* set remaining fields */
1229
1230	pdb_set_nt_passwd (sam_pass, pw_buf->smb_nt_passwd, PDB_SET);
1231	pdb_set_lanman_passwd (sam_pass, pw_buf->smb_passwd, PDB_SET);
1232	pdb_set_acct_ctrl (sam_pass, pw_buf->acct_ctrl, PDB_SET);
1233	pdb_set_pass_last_set_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
1234	pdb_set_pass_can_change_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
1235
1236	return True;
1237}
1238
1239/*****************************************************************
1240 Functions to be implemented by the new passdb API
1241 ****************************************************************/
1242
1243static NTSTATUS smbpasswd_setsampwent (struct pdb_methods *my_methods, BOOL update, uint16 acb_mask)
1244{
1245	struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1246
1247	smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file,
1248						       update ? PWF_UPDATE : PWF_READ,
1249						       &(smbpasswd_state->pw_file_lock_depth));
1250
1251	/* did we fail?  Should we try to create it? */
1252	if (!smbpasswd_state->pw_file && update && errno == ENOENT) {
1253		FILE *fp;
1254		/* slprintf(msg_str,msg_str_len-1,
1255		   "smbpasswd file did not exist - attempting to create it.\n"); */
1256		DEBUG(0,("smbpasswd file did not exist - attempting to create it.\n"));
1257		fp = sys_fopen(smbpasswd_state->smbpasswd_file, "w");
1258		if (fp) {
1259			fprintf(fp, "# Samba SMB password file\n");
1260			fclose(fp);
1261		}
1262
1263		smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file,
1264							     update ? PWF_UPDATE : PWF_READ,
1265							     &(smbpasswd_state->pw_file_lock_depth));
1266	}
1267
1268	if (smbpasswd_state->pw_file != NULL)
1269		return NT_STATUS_OK;
1270	else
1271		return NT_STATUS_UNSUCCESSFUL;
1272}
1273
1274static void smbpasswd_endsampwent (struct pdb_methods *my_methods)
1275{
1276	struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1277	endsmbfilepwent(smbpasswd_state->pw_file, &(smbpasswd_state->pw_file_lock_depth));
1278}
1279
1280/*****************************************************************
1281 ****************************************************************/
1282
1283static NTSTATUS smbpasswd_getsampwent(struct pdb_methods *my_methods, SAM_ACCOUNT *user)
1284{
1285	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1286	struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1287	struct smb_passwd *pw_buf=NULL;
1288	BOOL done = False;
1289	DEBUG(5,("pdb_getsampwent\n"));
1290
1291	if (user==NULL) {
1292		DEBUG(5,("pdb_getsampwent (smbpasswd): user is NULL\n"));
1293#if 0
1294		smb_panic("NULL pointer passed to getsampwent (smbpasswd)\n");
1295#endif
1296		return nt_status;
1297	}
1298
1299	while (!done) {
1300		/* do we have an entry? */
1301		pw_buf = getsmbfilepwent(smbpasswd_state, smbpasswd_state->pw_file);
1302		if (pw_buf == NULL)
1303			return nt_status;
1304
1305		/* build the SAM_ACCOUNT entry from the smb_passwd struct.
1306		   We loop in case the user in the pdb does not exist in
1307		   the local system password file */
1308		if (build_sam_account(smbpasswd_state, user, pw_buf))
1309			done = True;
1310	}
1311
1312	DEBUG(5,("getsampwent (smbpasswd): done\n"));
1313
1314	/* success */
1315	return NT_STATUS_OK;
1316}
1317
1318/****************************************************************
1319 Search smbpasswd file by iterating over the entries.  Do not
1320 call getpwnam() for unix account information until we have found
1321 the correct entry
1322 ***************************************************************/
1323
1324static NTSTATUS smbpasswd_getsampwnam(struct pdb_methods *my_methods,
1325				  SAM_ACCOUNT *sam_acct, const char *username)
1326{
1327	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1328	struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1329	struct smb_passwd *smb_pw;
1330	void *fp = NULL;
1331
1332	DEBUG(10, ("getsampwnam (smbpasswd): search by name: %s\n", username));
1333
1334	/* startsmbfilepwent() is used here as we don't want to lookup
1335	   the UNIX account in the local system password file until
1336	   we have a match.  */
1337	fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1338
1339	if (fp == NULL) {
1340		DEBUG(0, ("Unable to open passdb database.\n"));
1341		return nt_status;
1342	}
1343
1344	while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL)&& (!strequal(smb_pw->smb_name, username)) )
1345		/* do nothing....another loop */ ;
1346
1347	endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1348
1349
1350	/* did we locate the username in smbpasswd  */
1351	if (smb_pw == NULL)
1352		return nt_status;
1353
1354	DEBUG(10, ("getsampwnam (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1355
1356	if (!sam_acct) {
1357		DEBUG(10,("getsampwnam (smbpasswd): SAM_ACCOUNT is NULL\n"));
1358#if 0
1359		smb_panic("NULL pointer passed to pdb_getsampwnam\n");
1360#endif
1361		return nt_status;
1362	}
1363
1364	/* now build the SAM_ACCOUNT */
1365	if (!build_sam_account(smbpasswd_state, sam_acct, smb_pw))
1366		return nt_status;
1367
1368	/* success */
1369	return NT_STATUS_OK;
1370}
1371
1372static NTSTATUS smbpasswd_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT *sam_acct, const DOM_SID *sid)
1373{
1374	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1375	struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1376	struct smb_passwd *smb_pw;
1377	void *fp = NULL;
1378	fstring sid_str;
1379	uint32 rid;
1380
1381	DEBUG(10, ("smbpasswd_getsampwrid: search by sid: %s\n", sid_to_string(sid_str, sid)));
1382
1383	if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
1384		return NT_STATUS_UNSUCCESSFUL;
1385
1386	/* More special case 'guest account' hacks... */
1387	if (rid == DOMAIN_USER_RID_GUEST) {
1388		const char *guest_account = lp_guestaccount();
1389		if (!(guest_account && *guest_account)) {
1390			DEBUG(1, ("Guest account not specfied!\n"));
1391			return nt_status;
1392		}
1393		return smbpasswd_getsampwnam(my_methods, sam_acct, guest_account);
1394	}
1395
1396	/* Open the sam password file - not for update. */
1397	fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1398
1399	if (fp == NULL) {
1400		DEBUG(0, ("Unable to open passdb database.\n"));
1401		return nt_status;
1402	}
1403
1404	while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL) && (algorithmic_pdb_uid_to_user_rid(smb_pw->smb_userid) != rid) )
1405      		/* do nothing */ ;
1406
1407	endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1408
1409
1410	/* did we locate the username in smbpasswd  */
1411	if (smb_pw == NULL)
1412		return nt_status;
1413
1414	DEBUG(10, ("getsampwrid (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1415
1416	if (!sam_acct) {
1417		DEBUG(10,("getsampwrid: (smbpasswd) SAM_ACCOUNT is NULL\n"));
1418#if 0
1419		smb_panic("NULL pointer passed to pdb_getsampwrid\n");
1420#endif
1421		return nt_status;
1422	}
1423
1424	/* now build the SAM_ACCOUNT */
1425	if (!build_sam_account (smbpasswd_state, sam_acct, smb_pw))
1426		return nt_status;
1427
1428	/* build_sam_account might change the SID on us, if the name was for the guest account */
1429	if (NT_STATUS_IS_OK(nt_status) && !sid_equal(pdb_get_user_sid(sam_acct), sid)) {
1430		fstring sid_string1, sid_string2;
1431		DEBUG(1, ("looking for user with sid %s instead returned %s for account %s!?!\n",
1432			  sid_to_string(sid_string1, sid), sid_to_string(sid_string2, pdb_get_user_sid(sam_acct)), pdb_get_username(sam_acct)));
1433		return NT_STATUS_NO_SUCH_USER;
1434	}
1435
1436	/* success */
1437	return NT_STATUS_OK;
1438}
1439
1440static NTSTATUS smbpasswd_add_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
1441{
1442	struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1443	struct smb_passwd smb_pw;
1444
1445	/* convert the SAM_ACCOUNT */
1446	if (!build_smb_pass(&smb_pw, sampass)) {
1447		return NT_STATUS_UNSUCCESSFUL;
1448	}
1449
1450	/* add the entry */
1451	if(!add_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
1452		return NT_STATUS_UNSUCCESSFUL;
1453	}
1454
1455	return NT_STATUS_OK;
1456}
1457
1458static NTSTATUS smbpasswd_update_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
1459{
1460	struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1461	struct smb_passwd smb_pw;
1462
1463	/* convert the SAM_ACCOUNT */
1464	if (!build_smb_pass(&smb_pw, sampass)) {
1465		DEBUG(0, ("smbpasswd_update_sam_account: build_smb_pass failed!\n"));
1466		return NT_STATUS_UNSUCCESSFUL;
1467	}
1468
1469	/* update the entry */
1470	if(!mod_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
1471		DEBUG(0, ("smbpasswd_update_sam_account: mod_smbfilepwd_entry failed!\n"));
1472		return NT_STATUS_UNSUCCESSFUL;
1473	}
1474
1475	return NT_STATUS_OK;
1476}
1477
1478static NTSTATUS smbpasswd_delete_sam_account (struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
1479{
1480	struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1481
1482	const char *username = pdb_get_username(sampass);
1483
1484	if (del_smbfilepwd_entry(smbpasswd_state, username))
1485		return NT_STATUS_OK;
1486
1487	return NT_STATUS_UNSUCCESSFUL;
1488}
1489
1490static void free_private_data(void **vp)
1491{
1492	struct smbpasswd_privates **privates = (struct smbpasswd_privates**)vp;
1493
1494	endsmbfilepwent((*privates)->pw_file, &((*privates)->pw_file_lock_depth));
1495
1496	*privates = NULL;
1497	/* No need to free any further, as it is talloc()ed */
1498}
1499
1500static NTSTATUS pdb_init_smbpasswd(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
1501{
1502	NTSTATUS nt_status;
1503	struct smbpasswd_privates *privates;
1504
1505	if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
1506		return nt_status;
1507	}
1508
1509	(*pdb_method)->name = "smbpasswd";
1510
1511	(*pdb_method)->setsampwent = smbpasswd_setsampwent;
1512	(*pdb_method)->endsampwent = smbpasswd_endsampwent;
1513	(*pdb_method)->getsampwent = smbpasswd_getsampwent;
1514	(*pdb_method)->getsampwnam = smbpasswd_getsampwnam;
1515	(*pdb_method)->getsampwsid = smbpasswd_getsampwsid;
1516	(*pdb_method)->add_sam_account = smbpasswd_add_sam_account;
1517	(*pdb_method)->update_sam_account = smbpasswd_update_sam_account;
1518	(*pdb_method)->delete_sam_account = smbpasswd_delete_sam_account;
1519
1520	/* Setup private data and free function */
1521
1522	privates = TALLOC_ZERO_P(pdb_context->mem_ctx, struct smbpasswd_privates);
1523
1524	if (!privates) {
1525		DEBUG(0, ("talloc() failed for smbpasswd private_data!\n"));
1526		return NT_STATUS_NO_MEMORY;
1527	}
1528
1529	/* Store some config details */
1530
1531	if (location) {
1532		privates->smbpasswd_file = talloc_strdup(pdb_context->mem_ctx, location);
1533	} else {
1534		privates->smbpasswd_file = talloc_strdup(pdb_context->mem_ctx, lp_smb_passwd_file());
1535	}
1536
1537	if (!privates->smbpasswd_file) {
1538		DEBUG(0, ("talloc_strdp() failed for storing smbpasswd location!\n"));
1539		return NT_STATUS_NO_MEMORY;
1540	}
1541
1542	(*pdb_method)->private_data = privates;
1543
1544	(*pdb_method)->free_private_data = free_private_data;
1545
1546	return NT_STATUS_OK;
1547}
1548
1549NTSTATUS pdb_smbpasswd_init(void)
1550{
1551	return smb_register_passdb(PASSDB_INTERFACE_VERSION, "smbpasswd", pdb_init_smbpasswd);
1552}
1553