s3/libads: use monotonic clock for DNS timeouts
[Samba.git] / source3 / passdb / pdb_smbpasswd.c
blobdd89c8e10bafe5506ab49b9dd2a9ad44f7f818ae
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 3 of the License, or (at your option)
12 * any later version.
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.
19 * You should have received a copy of the GNU General Public License along with
20 * this program; if not, see <http://www.gnu.org/licenses/>.
23 #include "includes.h"
24 #include "../librpc/gen_ndr/samr.h"
26 #undef DBGC_CLASS
27 #define DBGC_CLASS DBGC_PASSDB
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
35 struct smb_passwd
37 uint32 smb_userid; /* this is actually the unix uid_t */
38 const char *smb_name; /* username string */
40 const unsigned char *smb_passwd; /* Null if no password */
41 const unsigned char *smb_nt_passwd; /* Null if no password */
43 uint16_t acct_ctrl; /* account info (ACB_xxxx bit-mask) */
44 time_t pass_last_set_time; /* password last set time */
47 struct smbpasswd_privates
49 /* used for maintain locks on the smbpasswd file */
50 int pw_file_lock_depth;
52 /* Global File pointer */
53 FILE *pw_file;
55 /* formerly static variables */
56 struct smb_passwd pw_buf;
57 fstring user_name;
58 unsigned char smbpwd[16];
59 unsigned char smbntpwd[16];
61 /* retrive-once info */
62 const char *smbpasswd_file;
65 enum pwf_access_type { PWF_READ, PWF_UPDATE, PWF_CREATE };
67 static SIG_ATOMIC_T gotalarm;
69 /***************************************************************
70 Signal function to tell us we timed out.
71 ****************************************************************/
73 static void gotalarm_sig(int signum)
75 gotalarm = 1;
78 /***************************************************************
79 Lock or unlock a fd for a known lock type. Abandon after waitsecs
80 seconds.
81 ****************************************************************/
83 static bool do_file_lock(int fd, int waitsecs, int type)
85 SMB_STRUCT_FLOCK lock;
86 int ret;
87 void (*oldsig_handler)(int);
89 gotalarm = 0;
90 oldsig_handler = CatchSignal(SIGALRM, gotalarm_sig);
92 lock.l_type = type;
93 lock.l_whence = SEEK_SET;
94 lock.l_start = 0;
95 lock.l_len = 1;
96 lock.l_pid = 0;
98 alarm(waitsecs);
99 /* Note we must *NOT* use sys_fcntl here ! JRA */
100 ret = fcntl(fd, SMB_F_SETLKW, &lock);
101 alarm(0);
102 CatchSignal(SIGALRM, oldsig_handler);
104 if (gotalarm && ret == -1) {
105 DEBUG(0, ("do_file_lock: failed to %s file.\n",
106 type == F_UNLCK ? "unlock" : "lock"));
107 return False;
110 return (ret == 0);
113 /***************************************************************
114 Lock an fd. Abandon after waitsecs seconds.
115 ****************************************************************/
117 static bool pw_file_lock(int fd, int type, int secs, int *plock_depth)
119 if (fd < 0) {
120 return False;
123 if(*plock_depth == 0) {
124 if (!do_file_lock(fd, secs, type)) {
125 DEBUG(10,("pw_file_lock: locking file failed, error = %s.\n",
126 strerror(errno)));
127 return False;
131 (*plock_depth)++;
133 return True;
136 /***************************************************************
137 Unlock an fd. Abandon after waitsecs seconds.
138 ****************************************************************/
140 static bool pw_file_unlock(int fd, int *plock_depth)
142 bool ret=True;
144 if (fd == 0 || *plock_depth == 0) {
145 return True;
148 if(*plock_depth == 1) {
149 ret = do_file_lock(fd, 5, F_UNLCK);
152 if (*plock_depth > 0) {
153 (*plock_depth)--;
156 if(!ret) {
157 DEBUG(10,("pw_file_unlock: unlocking file failed, error = %s.\n",
158 strerror(errno)));
160 return ret;
163 /**************************************************************
164 Intialize a smb_passwd struct
165 *************************************************************/
167 static void pdb_init_smb(struct smb_passwd *user)
169 if (user == NULL)
170 return;
171 ZERO_STRUCTP (user);
173 user->pass_last_set_time = (time_t)0;
176 /***************************************************************
177 Internal fn to enumerate the smbpasswd list. Returns a void pointer
178 to ensure no modification outside this module. Checks for atomic
179 rename of smbpasswd file on update or create once the lock has
180 been granted to prevent race conditions. JRA.
181 ****************************************************************/
183 static FILE *startsmbfilepwent(const char *pfile, enum pwf_access_type type, int *lock_depth)
185 FILE *fp = NULL;
186 const char *open_mode = NULL;
187 int race_loop = 0;
188 int lock_type = F_RDLCK;
190 if (!*pfile) {
191 DEBUG(0, ("startsmbfilepwent: No SMB password file set\n"));
192 return (NULL);
195 switch(type) {
196 case PWF_READ:
197 open_mode = "rb";
198 lock_type = F_RDLCK;
199 break;
200 case PWF_UPDATE:
201 open_mode = "r+b";
202 lock_type = F_WRLCK;
203 break;
204 case PWF_CREATE:
206 * Ensure atomic file creation.
209 int i, fd = -1;
211 for(i = 0; i < 5; i++) {
212 if((fd = sys_open(pfile, O_CREAT|O_TRUNC|O_EXCL|O_RDWR, 0600))!=-1) {
213 break;
215 sys_usleep(200); /* Spin, spin... */
217 if(fd == -1) {
218 DEBUG(0,("startsmbfilepwent_internal: too many race conditions \
219 creating file %s\n", pfile));
220 return NULL;
222 close(fd);
223 open_mode = "r+b";
224 lock_type = F_WRLCK;
225 break;
229 for(race_loop = 0; race_loop < 5; race_loop++) {
230 DEBUG(10, ("startsmbfilepwent_internal: opening file %s\n", pfile));
232 if((fp = sys_fopen(pfile, open_mode)) == NULL) {
235 * If smbpasswd file doesn't exist, then create new one. This helps to avoid
236 * confusing error msg when adding user account first time.
238 if (errno == ENOENT) {
239 if ((fp = sys_fopen(pfile, "a+")) != NULL) {
240 DEBUG(0, ("startsmbfilepwent_internal: file %s did not \
241 exist. File successfully created.\n", pfile));
242 } else {
243 DEBUG(0, ("startsmbfilepwent_internal: file %s did not \
244 exist. Couldn't create new one. Error was: %s",
245 pfile, strerror(errno)));
246 return NULL;
248 } else {
249 DEBUG(0, ("startsmbfilepwent_internal: unable to open file %s. \
250 Error was: %s\n", pfile, strerror(errno)));
251 return NULL;
255 if (!pw_file_lock(fileno(fp), lock_type, 5, lock_depth)) {
256 DEBUG(0, ("startsmbfilepwent_internal: unable to lock file %s. \
257 Error was %s\n", pfile, strerror(errno) ));
258 fclose(fp);
259 return NULL;
263 * Only check for replacement races on update or create.
264 * For read we don't mind if the data is one record out of date.
267 if(type == PWF_READ) {
268 break;
269 } else {
270 SMB_STRUCT_STAT sbuf1, sbuf2;
273 * Avoid the potential race condition between the open and the lock
274 * by doing a stat on the filename and an fstat on the fd. If the
275 * two inodes differ then someone did a rename between the open and
276 * the lock. Back off and try the open again. Only do this 5 times to
277 * prevent infinate loops. JRA.
280 if (sys_stat(pfile, &sbuf1, false) != 0) {
281 DEBUG(0, ("startsmbfilepwent_internal: unable to stat file %s. \
282 Error was %s\n", pfile, strerror(errno)));
283 pw_file_unlock(fileno(fp), lock_depth);
284 fclose(fp);
285 return NULL;
288 if (sys_fstat(fileno(fp), &sbuf2, false) != 0) {
289 DEBUG(0, ("startsmbfilepwent_internal: unable to fstat file %s. \
290 Error was %s\n", pfile, strerror(errno)));
291 pw_file_unlock(fileno(fp), lock_depth);
292 fclose(fp);
293 return NULL;
296 if( sbuf1.st_ex_ino == sbuf2.st_ex_ino) {
297 /* No race. */
298 break;
302 * Race occurred - back off and try again...
305 pw_file_unlock(fileno(fp), lock_depth);
306 fclose(fp);
310 if(race_loop == 5) {
311 DEBUG(0, ("startsmbfilepwent_internal: too many race conditions opening file %s\n", pfile));
312 return NULL;
315 /* Set a buffer to do more efficient reads */
316 setvbuf(fp, (char *)NULL, _IOFBF, 1024);
318 /* Make sure it is only rw by the owner */
319 #ifdef HAVE_FCHMOD
320 if(fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) {
321 #else
322 if(chmod(pfile, S_IRUSR|S_IWUSR) == -1) {
323 #endif
324 DEBUG(0, ("startsmbfilepwent_internal: failed to set 0600 permissions on password file %s. \
325 Error was %s\n.", pfile, strerror(errno) ));
326 pw_file_unlock(fileno(fp), lock_depth);
327 fclose(fp);
328 return NULL;
331 /* We have a lock on the file. */
332 return fp;
335 /***************************************************************
336 End enumeration of the smbpasswd list.
337 ****************************************************************/
339 static void endsmbfilepwent(FILE *fp, int *lock_depth)
341 if (!fp) {
342 return;
345 pw_file_unlock(fileno(fp), lock_depth);
346 fclose(fp);
347 DEBUG(7, ("endsmbfilepwent_internal: closed password file.\n"));
350 /*************************************************************************
351 Routine to return the next entry in the smbpasswd list.
352 *************************************************************************/
354 static struct smb_passwd *getsmbfilepwent(struct smbpasswd_privates *smbpasswd_state, FILE *fp)
356 /* Static buffers we will return. */
357 struct smb_passwd *pw_buf = &smbpasswd_state->pw_buf;
358 char *user_name = smbpasswd_state->user_name;
359 unsigned char *smbpwd = smbpasswd_state->smbpwd;
360 unsigned char *smbntpwd = smbpasswd_state->smbntpwd;
361 char linebuf[256];
362 int c;
363 unsigned char *p;
364 long uidval;
365 size_t linebuf_len;
366 char *status;
368 if(fp == NULL) {
369 DEBUG(0,("getsmbfilepwent: Bad password file pointer.\n"));
370 return NULL;
373 pdb_init_smb(pw_buf);
374 pw_buf->acct_ctrl = ACB_NORMAL;
377 * Scan the file, a line at a time and check if the name matches.
379 status = linebuf;
380 while (status && !feof(fp)) {
381 linebuf[0] = '\0';
383 status = fgets(linebuf, 256, fp);
384 if (status == NULL && ferror(fp)) {
385 return NULL;
389 * Check if the string is terminated with a newline - if not
390 * then we must keep reading and discard until we get one.
392 if ((linebuf_len = strlen(linebuf)) == 0) {
393 continue;
396 if (linebuf[linebuf_len - 1] != '\n') {
397 c = '\0';
398 while (!ferror(fp) && !feof(fp)) {
399 c = fgetc(fp);
400 if (c == '\n') {
401 break;
404 } else {
405 linebuf[linebuf_len - 1] = '\0';
408 #ifdef DEBUG_PASSWORD
409 DEBUG(100, ("getsmbfilepwent: got line |%s|\n", linebuf));
410 #endif
411 if ((linebuf[0] == 0) && feof(fp)) {
412 DEBUG(4, ("getsmbfilepwent: end of file reached\n"));
413 break;
417 * The line we have should be of the form :-
419 * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently
420 * ignored....
422 * or,
424 * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored....
426 * if Windows NT compatible passwords are also present.
427 * [Account type] is an ascii encoding of the type of account.
428 * LCT-(8 hex digits) is the time_t value of the last change time.
431 if (linebuf[0] == '#' || linebuf[0] == '\0') {
432 DEBUG(6, ("getsmbfilepwent: skipping comment or blank line\n"));
433 continue;
435 p = (unsigned char *) strchr_m(linebuf, ':');
436 if (p == NULL) {
437 DEBUG(0, ("getsmbfilepwent: malformed password entry (no :)\n"));
438 continue;
441 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
442 user_name[PTR_DIFF(p, linebuf)] = '\0';
444 /* Get smb uid. */
446 p++; /* Go past ':' */
448 if(*p == '-') {
449 DEBUG(0, ("getsmbfilepwent: user name %s has a negative uid.\n", user_name));
450 continue;
453 if (!isdigit(*p)) {
454 DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (uid not number)\n",
455 user_name));
456 continue;
459 uidval = atoi((char *) p);
461 while (*p && isdigit(*p)) {
462 p++;
465 if (*p != ':') {
466 DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (no : after uid)\n",
467 user_name));
468 continue;
471 pw_buf->smb_name = user_name;
472 pw_buf->smb_userid = uidval;
475 * Now get the password value - this should be 32 hex digits
476 * which are the ascii representations of a 16 byte string.
477 * Get two at a time and put them into the password.
480 /* Skip the ':' */
481 p++;
483 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
484 DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (passwd too short)\n",
485 user_name ));
486 continue;
489 if (p[32] != ':') {
490 DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (no terminating :)\n",
491 user_name));
492 continue;
495 if (strnequal((char *) p, "NO PASSWORD", 11)) {
496 pw_buf->smb_passwd = NULL;
497 pw_buf->acct_ctrl |= ACB_PWNOTREQ;
498 } else {
499 if (*p == '*' || *p == 'X') {
500 /* NULL LM password */
501 pw_buf->smb_passwd = NULL;
502 DEBUG(10, ("getsmbfilepwent: LM password for user %s invalidated\n", user_name));
503 } else if (pdb_gethexpwd((char *)p, smbpwd)) {
504 pw_buf->smb_passwd = smbpwd;
505 } else {
506 pw_buf->smb_passwd = NULL;
507 DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry for user %s \
508 (non hex chars)\n", user_name));
513 * Now check if the NT compatible password is
514 * available.
516 pw_buf->smb_nt_passwd = NULL;
517 p += 33; /* Move to the first character of the line after the lanman password. */
518 if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
519 if (*p != '*' && *p != 'X') {
520 if(pdb_gethexpwd((char *)p,smbntpwd)) {
521 pw_buf->smb_nt_passwd = smbntpwd;
524 p += 33; /* Move to the first character of the line after the NT password. */
527 DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n",
528 user_name, uidval));
530 if (*p == '[') {
531 unsigned char *end_p = (unsigned char *)strchr_m((char *)p, ']');
532 pw_buf->acct_ctrl = pdb_decode_acct_ctrl((char*)p);
534 /* Must have some account type set. */
535 if(pw_buf->acct_ctrl == 0) {
536 pw_buf->acct_ctrl = ACB_NORMAL;
539 /* Now try and get the last change time. */
540 if(end_p) {
541 p = end_p + 1;
543 if(*p == ':') {
544 p++;
545 if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) {
546 int i;
547 p += 4;
548 for(i = 0; i < 8; i++) {
549 if(p[i] == '\0' || !isxdigit(p[i])) {
550 break;
553 if(i == 8) {
555 * p points at 8 characters of hex digits -
556 * read into a time_t as the seconds since
557 * 1970 that the password was last changed.
559 pw_buf->pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
563 } else {
564 /* 'Old' style file. Fake up based on user name. */
566 * Currently trust accounts are kept in the same
567 * password file as 'normal accounts'. If this changes
568 * we will have to fix this code. JRA.
570 if(pw_buf->smb_name[strlen(pw_buf->smb_name) - 1] == '$') {
571 pw_buf->acct_ctrl &= ~ACB_NORMAL;
572 pw_buf->acct_ctrl |= ACB_WSTRUST;
576 return pw_buf;
579 DEBUG(5,("getsmbfilepwent: end of file reached.\n"));
580 return NULL;
583 /************************************************************************
584 Create a new smbpasswd entry - malloced space returned.
585 *************************************************************************/
587 static char *format_new_smbpasswd_entry(const struct smb_passwd *newpwd)
589 int new_entry_length;
590 char *new_entry;
591 char *p;
593 new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 +
594 NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2;
596 if((new_entry = (char *)SMB_MALLOC( new_entry_length )) == NULL) {
597 DEBUG(0, ("format_new_smbpasswd_entry: Malloc failed adding entry for user %s.\n",
598 newpwd->smb_name ));
599 return NULL;
602 slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid);
604 p = new_entry+strlen(new_entry);
605 pdb_sethexpwd(p, newpwd->smb_passwd, newpwd->acct_ctrl);
606 p+=strlen(p);
607 *p = ':';
608 p++;
610 pdb_sethexpwd(p, newpwd->smb_nt_passwd, newpwd->acct_ctrl);
611 p+=strlen(p);
612 *p = ':';
613 p++;
615 /* Add the account encoding and the last change time. */
616 slprintf((char *)p, new_entry_length - 1 - (p - new_entry), "%s:LCT-%08X:\n",
617 pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN),
618 (uint32_t)newpwd->pass_last_set_time);
620 return new_entry;
623 /************************************************************************
624 Routine to add an entry to the smbpasswd file.
625 *************************************************************************/
627 static NTSTATUS add_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state,
628 struct smb_passwd *newpwd)
630 const char *pfile = smbpasswd_state->smbpasswd_file;
631 struct smb_passwd *pwd = NULL;
632 FILE *fp = NULL;
633 int wr_len;
634 int fd;
635 size_t new_entry_length;
636 char *new_entry;
637 SMB_OFF_T offpos;
639 /* Open the smbpassword file - for update. */
640 fp = startsmbfilepwent(pfile, PWF_UPDATE, &smbpasswd_state->pw_file_lock_depth);
642 if (fp == NULL && errno == ENOENT) {
643 /* Try again - create. */
644 fp = startsmbfilepwent(pfile, PWF_CREATE, &smbpasswd_state->pw_file_lock_depth);
647 if (fp == NULL) {
648 DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n"));
649 return map_nt_error_from_unix(errno);
653 * Scan the file, a line at a time and check if the name matches.
656 while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
657 if (strequal(newpwd->smb_name, pwd->smb_name)) {
658 DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name));
659 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
660 return NT_STATUS_USER_EXISTS;
664 /* Ok - entry doesn't exist. We can add it */
666 /* Create a new smb passwd entry and set it to the given password. */
668 * The add user write needs to be atomic - so get the fd from
669 * the fp and do a raw write() call.
671 fd = fileno(fp);
673 if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1) {
674 NTSTATUS result = map_nt_error_from_unix(errno);
675 DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \
676 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
677 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
678 return result;
681 if((new_entry = format_new_smbpasswd_entry(newpwd)) == NULL) {
682 DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
683 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
684 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
685 return NT_STATUS_NO_MEMORY;
688 new_entry_length = strlen(new_entry);
690 #ifdef DEBUG_PASSWORD
691 DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d made line |%s|",
692 fd, (int)new_entry_length, new_entry));
693 #endif
695 if ((wr_len = write(fd, new_entry, new_entry_length)) != new_entry_length) {
696 NTSTATUS result = map_nt_error_from_unix(errno);
697 DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \
698 Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
700 /* Remove the entry we just wrote. */
701 if(sys_ftruncate(fd, offpos) == -1) {
702 DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \
703 Error was %s. Password file may be corrupt ! Please examine by hand !\n",
704 newpwd->smb_name, strerror(errno)));
707 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
708 free(new_entry);
709 return result;
712 free(new_entry);
713 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
714 return NT_STATUS_OK;
717 /************************************************************************
718 Routine to search the smbpasswd file for an entry matching the username.
719 and then modify its password entry. We can't use the startsmbpwent()/
720 getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
721 in the actual file to decide how much room we have to write data.
722 override = False, normal
723 override = True, override XXXXXXXX'd out password or NO PASS
724 ************************************************************************/
726 static bool mod_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const struct smb_passwd* pwd)
728 /* Static buffers we will return. */
729 fstring user_name;
731 char *status;
732 char linebuf[256];
733 char readbuf[1024];
734 int c;
735 fstring ascii_p16;
736 fstring encode_bits;
737 unsigned char *p = NULL;
738 size_t linebuf_len = 0;
739 FILE *fp;
740 int lockfd;
741 const char *pfile = smbpasswd_state->smbpasswd_file;
742 bool found_entry = False;
743 bool got_pass_last_set_time = False;
745 SMB_OFF_T pwd_seekpos = 0;
747 int i;
748 int wr_len;
749 int fd;
751 if (!*pfile) {
752 DEBUG(0, ("No SMB password file set\n"));
753 return False;
755 DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
757 fp = sys_fopen(pfile, "r+");
759 if (fp == NULL) {
760 DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
761 return False;
763 /* Set a buffer to do more efficient reads */
764 setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
766 lockfd = fileno(fp);
768 if (!pw_file_lock(lockfd, F_WRLCK, 5, &smbpasswd_state->pw_file_lock_depth)) {
769 DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile));
770 fclose(fp);
771 return False;
774 /* Make sure it is only rw by the owner */
775 chmod(pfile, 0600);
777 /* We have a write lock on the file. */
779 * Scan the file, a line at a time and check if the name matches.
781 status = linebuf;
782 while (status && !feof(fp)) {
783 pwd_seekpos = sys_ftell(fp);
785 linebuf[0] = '\0';
787 status = fgets(linebuf, sizeof(linebuf), fp);
788 if (status == NULL && ferror(fp)) {
789 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
790 fclose(fp);
791 return False;
795 * Check if the string is terminated with a newline - if not
796 * then we must keep reading and discard until we get one.
798 linebuf_len = strlen(linebuf);
799 if (linebuf[linebuf_len - 1] != '\n') {
800 c = '\0';
801 while (!ferror(fp) && !feof(fp)) {
802 c = fgetc(fp);
803 if (c == '\n') {
804 break;
807 } else {
808 linebuf[linebuf_len - 1] = '\0';
811 #ifdef DEBUG_PASSWORD
812 DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
813 #endif
815 if ((linebuf[0] == 0) && feof(fp)) {
816 DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
817 break;
821 * The line we have should be of the form :-
823 * username:uid:[32hex bytes]:....other flags presently
824 * ignored....
826 * or,
828 * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
830 * if Windows NT compatible passwords are also present.
833 if (linebuf[0] == '#' || linebuf[0] == '\0') {
834 DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
835 continue;
838 p = (unsigned char *) strchr_m(linebuf, ':');
840 if (p == NULL) {
841 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
842 continue;
845 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
846 user_name[PTR_DIFF(p, linebuf)] = '\0';
847 if (strequal(user_name, pwd->smb_name)) {
848 found_entry = True;
849 break;
853 if (!found_entry) {
854 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
855 fclose(fp);
857 DEBUG(2, ("Cannot update entry for user %s, as they don't exist in the smbpasswd file!\n",
858 pwd->smb_name));
859 return False;
862 DEBUG(6, ("mod_smbfilepwd_entry: entry exists for user %s\n", pwd->smb_name));
864 /* User name matches - get uid and password */
865 p++; /* Go past ':' */
867 if (!isdigit(*p)) {
868 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (uid not number)\n",
869 pwd->smb_name));
870 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
871 fclose(fp);
872 return False;
875 while (*p && isdigit(*p)) {
876 p++;
878 if (*p != ':') {
879 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no : after uid)\n",
880 pwd->smb_name));
881 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
882 fclose(fp);
883 return False;
887 * Now get the password value - this should be 32 hex digits
888 * which are the ascii representations of a 16 byte string.
889 * Get two at a time and put them into the password.
891 p++;
893 /* Record exact password position */
894 pwd_seekpos += PTR_DIFF(p, linebuf);
896 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
897 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (passwd too short)\n",
898 pwd->smb_name));
899 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
900 fclose(fp);
901 return (False);
904 if (p[32] != ':') {
905 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no terminating :)\n",
906 pwd->smb_name));
907 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
908 fclose(fp);
909 return False;
912 /* Now check if the NT compatible password is available. */
913 p += 33; /* Move to the first character of the line after the lanman password. */
914 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
915 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (passwd too short)\n",
916 pwd->smb_name));
917 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
918 fclose(fp);
919 return (False);
922 if (p[32] != ':') {
923 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no terminating :)\n",
924 pwd->smb_name));
925 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
926 fclose(fp);
927 return False;
931 * Now check if the account info and the password last
932 * change time is available.
934 p += 33; /* Move to the first character of the line after the NT password. */
936 if (*p == '[') {
937 i = 0;
938 encode_bits[i++] = *p++;
939 while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']')) {
940 encode_bits[i++] = *p++;
943 encode_bits[i++] = ']';
944 encode_bits[i++] = '\0';
946 if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
948 * We are using a new format, space padded
949 * acct ctrl field. Encode the given acct ctrl
950 * bits into it.
952 fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
953 } else {
954 DEBUG(0,("mod_smbfilepwd_entry: Using old smbpasswd format for user %s. \
955 This is no longer supported.!\n", pwd->smb_name));
956 DEBUG(0,("mod_smbfilepwd_entry: No changes made, failing.!\n"));
957 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
958 fclose(fp);
959 return False;
962 /* Go past the ']' */
963 if(linebuf_len > PTR_DIFF(p, linebuf)) {
964 p++;
967 if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
968 p++;
970 /* We should be pointing at the LCT entry. */
971 if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
972 p += 4;
973 for(i = 0; i < 8; i++) {
974 if(p[i] == '\0' || !isxdigit(p[i])) {
975 break;
978 if(i == 8) {
980 * p points at 8 characters of hex digits -
981 * read into a time_t as the seconds since
982 * 1970 that the password was last changed.
984 got_pass_last_set_time = True;
985 } /* i == 8 */
986 } /* *p && StrnCaseCmp() */
987 } /* p == ':' */
988 } /* p == '[' */
990 /* Entry is correctly formed. */
992 /* Create the 32 byte representation of the new p16 */
993 pdb_sethexpwd(ascii_p16, pwd->smb_passwd, pwd->acct_ctrl);
995 /* Add on the NT md4 hash */
996 ascii_p16[32] = ':';
997 wr_len = 66;
998 pdb_sethexpwd(ascii_p16+33, pwd->smb_nt_passwd, pwd->acct_ctrl);
999 ascii_p16[65] = ':';
1000 ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
1002 /* Add on the account info bits and the time of last password change. */
1003 if(got_pass_last_set_time) {
1004 slprintf(&ascii_p16[strlen(ascii_p16)],
1005 sizeof(ascii_p16)-(strlen(ascii_p16)+1),
1006 "%s:LCT-%08X:",
1007 encode_bits, (uint32_t)pwd->pass_last_set_time );
1008 wr_len = strlen(ascii_p16);
1011 #ifdef DEBUG_PASSWORD
1012 DEBUG(100,("mod_smbfilepwd_entry: "));
1013 dump_data(100, (uint8 *)ascii_p16, wr_len);
1014 #endif
1016 if(wr_len > sizeof(linebuf)) {
1017 DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
1018 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1019 fclose(fp);
1020 return (False);
1024 * Do an atomic write into the file at the position defined by
1025 * seekpos.
1028 /* The mod user write needs to be atomic - so get the fd from
1029 the fp and do a raw write() call.
1032 fd = fileno(fp);
1034 if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
1035 DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1036 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1037 fclose(fp);
1038 return False;
1041 /* Sanity check - ensure the areas we are writing are framed by ':' */
1042 if (read(fd, linebuf, wr_len+1) != wr_len+1) {
1043 DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
1044 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1045 fclose(fp);
1046 return False;
1049 if ((linebuf[0] != ':') || (linebuf[wr_len] != ':')) {
1050 DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
1051 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1052 fclose(fp);
1053 return False;
1056 if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
1057 DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1058 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1059 fclose(fp);
1060 return False;
1063 if (write(fd, ascii_p16, wr_len) != wr_len) {
1064 DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
1065 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1066 fclose(fp);
1067 return False;
1070 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1071 fclose(fp);
1072 return True;
1075 /************************************************************************
1076 Routine to delete an entry in the smbpasswd file by name.
1077 *************************************************************************/
1079 static bool del_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const char *name)
1081 const char *pfile = smbpasswd_state->smbpasswd_file;
1082 char *pfile2 = NULL;
1083 struct smb_passwd *pwd = NULL;
1084 FILE *fp = NULL;
1085 FILE *fp_write = NULL;
1086 int pfile2_lockdepth = 0;
1088 pfile2 = talloc_asprintf(talloc_tos(),
1089 "%s.%u",
1090 pfile, (unsigned)sys_getpid());
1091 if (!pfile2) {
1092 return false;
1096 * Open the smbpassword file - for update. It needs to be update
1097 * as we need any other processes to wait until we have replaced
1098 * it.
1101 if((fp = startsmbfilepwent(pfile, PWF_UPDATE, &smbpasswd_state->pw_file_lock_depth)) == NULL) {
1102 DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1103 return False;
1107 * Create the replacement password file.
1109 if((fp_write = startsmbfilepwent(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) {
1110 DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1111 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1112 return False;
1116 * Scan the file, a line at a time and check if the name matches.
1119 while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
1120 char *new_entry;
1121 size_t new_entry_length;
1123 if (strequal(name, pwd->smb_name)) {
1124 DEBUG(10, ("del_smbfilepwd_entry: found entry with "
1125 "name %s - deleting it.\n", name));
1126 continue;
1130 * We need to copy the entry out into the second file.
1133 if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL) {
1134 DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \
1135 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1136 unlink(pfile2);
1137 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1138 endsmbfilepwent(fp_write, &pfile2_lockdepth);
1139 return False;
1142 new_entry_length = strlen(new_entry);
1144 if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length) {
1145 DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \
1146 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1147 unlink(pfile2);
1148 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1149 endsmbfilepwent(fp_write, &pfile2_lockdepth);
1150 free(new_entry);
1151 return False;
1154 free(new_entry);
1158 * Ensure pfile2 is flushed before rename.
1161 if(fflush(fp_write) != 0) {
1162 DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno)));
1163 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1164 endsmbfilepwent(fp_write,&pfile2_lockdepth);
1165 return False;
1169 * Do an atomic rename - then release the locks.
1172 if(rename(pfile2,pfile) != 0) {
1173 unlink(pfile2);
1176 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1177 endsmbfilepwent(fp_write,&pfile2_lockdepth);
1178 return True;
1181 /*********************************************************************
1182 Create a smb_passwd struct from a struct samu.
1183 We will not allocate any new memory. The smb_passwd struct
1184 should only stay around as long as the struct samu does.
1185 ********************************************************************/
1187 static bool build_smb_pass (struct smb_passwd *smb_pw, const struct samu *sampass)
1189 uint32_t rid;
1191 if (sampass == NULL)
1192 return False;
1193 ZERO_STRUCTP(smb_pw);
1195 if (!IS_SAM_DEFAULT(sampass, PDB_USERSID)) {
1196 rid = pdb_get_user_rid(sampass);
1198 /* If the user specified a RID, make sure its able to be both stored and retreived */
1199 if (rid == DOMAIN_RID_GUEST) {
1200 struct passwd *passwd = getpwnam_alloc(NULL, lp_guestaccount());
1201 if (!passwd) {
1202 DEBUG(0, ("Could not find guest account via getpwnam()! (%s)\n", lp_guestaccount()));
1203 return False;
1205 smb_pw->smb_userid=passwd->pw_uid;
1206 TALLOC_FREE(passwd);
1207 } else if (algorithmic_pdb_rid_is_user(rid)) {
1208 smb_pw->smb_userid=algorithmic_pdb_user_rid_to_uid(rid);
1209 } else {
1210 DEBUG(0,("build_sam_pass: Failing attempt to store user with non-uid based user RID. \n"));
1211 return False;
1215 smb_pw->smb_name=(const char*)pdb_get_username(sampass);
1217 smb_pw->smb_passwd=pdb_get_lanman_passwd(sampass);
1218 smb_pw->smb_nt_passwd=pdb_get_nt_passwd(sampass);
1220 smb_pw->acct_ctrl=pdb_get_acct_ctrl(sampass);
1221 smb_pw->pass_last_set_time=pdb_get_pass_last_set_time(sampass);
1223 return True;
1226 /*********************************************************************
1227 Create a struct samu from a smb_passwd struct
1228 ********************************************************************/
1230 static bool build_sam_account(struct smbpasswd_privates *smbpasswd_state,
1231 struct samu *sam_pass, const struct smb_passwd *pw_buf)
1233 struct passwd *pwfile;
1235 if ( !sam_pass ) {
1236 DEBUG(5,("build_sam_account: struct samu is NULL\n"));
1237 return False;
1240 /* verify the user account exists */
1242 if ( !(pwfile = Get_Pwnam_alloc(NULL, pw_buf->smb_name )) ) {
1243 DEBUG(0,("build_sam_account: smbpasswd database is corrupt! username %s with uid "
1244 "%u is not in unix passwd database!\n", pw_buf->smb_name, pw_buf->smb_userid));
1245 return False;
1248 if ( !NT_STATUS_IS_OK( samu_set_unix(sam_pass, pwfile )) )
1249 return False;
1251 TALLOC_FREE(pwfile);
1253 /* set remaining fields */
1255 if (!pdb_set_nt_passwd (sam_pass, pw_buf->smb_nt_passwd, PDB_SET))
1256 return False;
1257 if (!pdb_set_lanman_passwd (sam_pass, pw_buf->smb_passwd, PDB_SET))
1258 return False;
1259 pdb_set_acct_ctrl (sam_pass, pw_buf->acct_ctrl, PDB_SET);
1260 pdb_set_pass_last_set_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
1261 pdb_set_pass_can_change_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
1263 return True;
1266 /*****************************************************************
1267 Functions to be implemented by the new passdb API
1268 ****************************************************************/
1270 /****************************************************************
1271 Search smbpasswd file by iterating over the entries. Do not
1272 call getpwnam() for unix account information until we have found
1273 the correct entry
1274 ***************************************************************/
1276 static NTSTATUS smbpasswd_getsampwnam(struct pdb_methods *my_methods,
1277 struct samu *sam_acct, const char *username)
1279 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1280 struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1281 struct smb_passwd *smb_pw;
1282 FILE *fp = NULL;
1284 DEBUG(10, ("getsampwnam (smbpasswd): search by name: %s\n", username));
1286 /* startsmbfilepwent() is used here as we don't want to lookup
1287 the UNIX account in the local system password file until
1288 we have a match. */
1289 fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1291 if (fp == NULL) {
1292 DEBUG(0, ("Unable to open passdb database.\n"));
1293 return nt_status;
1296 while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL)&& (!strequal(smb_pw->smb_name, username)) )
1297 /* do nothing....another loop */ ;
1299 endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1302 /* did we locate the username in smbpasswd */
1303 if (smb_pw == NULL)
1304 return nt_status;
1306 DEBUG(10, ("getsampwnam (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1308 if (!sam_acct) {
1309 DEBUG(10,("getsampwnam (smbpasswd): struct samu is NULL\n"));
1310 return nt_status;
1313 /* now build the struct samu */
1314 if (!build_sam_account(smbpasswd_state, sam_acct, smb_pw))
1315 return nt_status;
1317 /* success */
1318 return NT_STATUS_OK;
1321 static NTSTATUS smbpasswd_getsampwsid(struct pdb_methods *my_methods, struct samu *sam_acct, const struct dom_sid *sid)
1323 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1324 struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1325 struct smb_passwd *smb_pw;
1326 FILE *fp = NULL;
1327 uint32_t rid;
1329 DEBUG(10, ("smbpasswd_getsampwrid: search by sid: %s\n",
1330 sid_string_dbg(sid)));
1332 if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
1333 return NT_STATUS_UNSUCCESSFUL;
1335 /* More special case 'guest account' hacks... */
1336 if (rid == DOMAIN_RID_GUEST) {
1337 const char *guest_account = lp_guestaccount();
1338 if (!(guest_account && *guest_account)) {
1339 DEBUG(1, ("Guest account not specfied!\n"));
1340 return nt_status;
1342 return smbpasswd_getsampwnam(my_methods, sam_acct, guest_account);
1345 /* Open the sam password file - not for update. */
1346 fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1348 if (fp == NULL) {
1349 DEBUG(0, ("Unable to open passdb database.\n"));
1350 return nt_status;
1353 while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL) && (algorithmic_pdb_uid_to_user_rid(smb_pw->smb_userid) != rid) )
1354 /* do nothing */ ;
1356 endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1359 /* did we locate the username in smbpasswd */
1360 if (smb_pw == NULL)
1361 return nt_status;
1363 DEBUG(10, ("getsampwrid (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1365 if (!sam_acct) {
1366 DEBUG(10,("getsampwrid: (smbpasswd) struct samu is NULL\n"));
1367 return nt_status;
1370 /* now build the struct samu */
1371 if (!build_sam_account (smbpasswd_state, sam_acct, smb_pw))
1372 return nt_status;
1374 /* build_sam_account might change the SID on us, if the name was for the guest account */
1375 if (NT_STATUS_IS_OK(nt_status) && !sid_equal(pdb_get_user_sid(sam_acct), sid)) {
1376 DEBUG(1, ("looking for user with sid %s instead returned %s "
1377 "for account %s!?!\n", sid_string_dbg(sid),
1378 sid_string_dbg(pdb_get_user_sid(sam_acct)),
1379 pdb_get_username(sam_acct)));
1380 return NT_STATUS_NO_SUCH_USER;
1383 /* success */
1384 return NT_STATUS_OK;
1387 static NTSTATUS smbpasswd_add_sam_account(struct pdb_methods *my_methods, struct samu *sampass)
1389 struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1390 struct smb_passwd smb_pw;
1392 /* convert the struct samu */
1393 if (!build_smb_pass(&smb_pw, sampass)) {
1394 return NT_STATUS_UNSUCCESSFUL;
1397 /* add the entry */
1398 return add_smbfilepwd_entry(smbpasswd_state, &smb_pw);
1401 static NTSTATUS smbpasswd_update_sam_account(struct pdb_methods *my_methods, struct samu *sampass)
1403 struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1404 struct smb_passwd smb_pw;
1406 /* convert the struct samu */
1407 if (!build_smb_pass(&smb_pw, sampass)) {
1408 DEBUG(0, ("smbpasswd_update_sam_account: build_smb_pass failed!\n"));
1409 return NT_STATUS_UNSUCCESSFUL;
1412 /* update the entry */
1413 if(!mod_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
1414 DEBUG(0, ("smbpasswd_update_sam_account: mod_smbfilepwd_entry failed!\n"));
1415 return NT_STATUS_UNSUCCESSFUL;
1418 return NT_STATUS_OK;
1421 static NTSTATUS smbpasswd_delete_sam_account (struct pdb_methods *my_methods, struct samu *sampass)
1423 struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1425 const char *username = pdb_get_username(sampass);
1427 if (del_smbfilepwd_entry(smbpasswd_state, username))
1428 return NT_STATUS_OK;
1430 return NT_STATUS_UNSUCCESSFUL;
1433 static NTSTATUS smbpasswd_rename_sam_account (struct pdb_methods *my_methods,
1434 struct samu *old_acct,
1435 const char *newname)
1437 char *rename_script = NULL;
1438 struct samu *new_acct = NULL;
1439 bool interim_account = False;
1440 TALLOC_CTX *ctx = talloc_tos();
1441 NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
1443 if (!*(lp_renameuser_script()))
1444 goto done;
1446 if ( !(new_acct = samu_new( NULL )) ) {
1447 return NT_STATUS_NO_MEMORY;
1450 if ( !pdb_copy_sam_account( new_acct, old_acct )
1451 || !pdb_set_username(new_acct, newname, PDB_CHANGED))
1453 goto done;
1456 ret = smbpasswd_add_sam_account(my_methods, new_acct);
1457 if (!NT_STATUS_IS_OK(ret))
1458 goto done;
1460 interim_account = True;
1462 /* rename the posix user */
1463 rename_script = talloc_strdup(ctx,
1464 lp_renameuser_script());
1465 if (!rename_script) {
1466 ret = NT_STATUS_NO_MEMORY;
1467 goto done;
1470 if (*rename_script) {
1471 int rename_ret;
1473 rename_script = talloc_string_sub2(ctx,
1474 rename_script,
1475 "%unew",
1476 newname,
1477 true,
1478 false,
1479 true);
1480 if (!rename_script) {
1481 ret = NT_STATUS_NO_MEMORY;
1482 goto done;
1484 rename_script = talloc_string_sub2(ctx,
1485 rename_script,
1486 "%uold",
1487 pdb_get_username(old_acct),
1488 true,
1489 false,
1490 true);
1491 if (!rename_script) {
1492 ret = NT_STATUS_NO_MEMORY;
1493 goto done;
1496 rename_ret = smbrun(rename_script, NULL);
1498 DEBUG(rename_ret ? 0 : 3,("Running the command `%s' gave %d\n", rename_script, rename_ret));
1500 if (rename_ret == 0) {
1501 smb_nscd_flush_user_cache();
1504 if (rename_ret)
1505 goto done;
1506 } else {
1507 goto done;
1510 smbpasswd_delete_sam_account(my_methods, old_acct);
1511 interim_account = False;
1513 done:
1514 /* cleanup */
1515 if (interim_account)
1516 smbpasswd_delete_sam_account(my_methods, new_acct);
1518 if (new_acct)
1519 TALLOC_FREE(new_acct);
1521 return (ret);
1524 static uint32_t smbpasswd_capabilities(struct pdb_methods *methods)
1526 return 0;
1529 static void free_private_data(void **vp)
1531 struct smbpasswd_privates **privates = (struct smbpasswd_privates**)vp;
1533 endsmbfilepwent((*privates)->pw_file, &((*privates)->pw_file_lock_depth));
1535 *privates = NULL;
1536 /* No need to free any further, as it is talloc()ed */
1539 struct smbpasswd_search_state {
1540 uint32_t acct_flags;
1542 struct samr_displayentry *entries;
1543 uint32_t num_entries;
1544 ssize_t array_size;
1545 uint32_t current;
1548 static void smbpasswd_search_end(struct pdb_search *search)
1550 struct smbpasswd_search_state *state = talloc_get_type_abort(
1551 search->private_data, struct smbpasswd_search_state);
1552 TALLOC_FREE(state);
1555 static bool smbpasswd_search_next_entry(struct pdb_search *search,
1556 struct samr_displayentry *entry)
1558 struct smbpasswd_search_state *state = talloc_get_type_abort(
1559 search->private_data, struct smbpasswd_search_state);
1561 if (state->current == state->num_entries) {
1562 return false;
1565 entry->idx = state->entries[state->current].idx;
1566 entry->rid = state->entries[state->current].rid;
1567 entry->acct_flags = state->entries[state->current].acct_flags;
1569 entry->account_name = talloc_strdup(
1570 search, state->entries[state->current].account_name);
1571 entry->fullname = talloc_strdup(
1572 search, state->entries[state->current].fullname);
1573 entry->description = talloc_strdup(
1574 search, state->entries[state->current].description);
1576 if ((entry->account_name == NULL) || (entry->fullname == NULL)
1577 || (entry->description == NULL)) {
1578 DEBUG(0, ("talloc_strdup failed\n"));
1579 return false;
1582 state->current += 1;
1583 return true;
1586 static bool smbpasswd_search_users(struct pdb_methods *methods,
1587 struct pdb_search *search,
1588 uint32_t acct_flags)
1590 struct smbpasswd_privates *smbpasswd_state =
1591 (struct smbpasswd_privates*)methods->private_data;
1593 struct smbpasswd_search_state *search_state;
1594 struct smb_passwd *pwd;
1595 FILE *fp;
1597 search_state = talloc_zero(search, struct smbpasswd_search_state);
1598 if (search_state == NULL) {
1599 DEBUG(0, ("talloc failed\n"));
1600 return false;
1602 search_state->acct_flags = acct_flags;
1604 fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ,
1605 &smbpasswd_state->pw_file_lock_depth);
1607 if (fp == NULL) {
1608 DEBUG(10, ("Unable to open smbpasswd file.\n"));
1609 TALLOC_FREE(search_state);
1610 return false;
1613 while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
1614 struct samr_displayentry entry;
1615 struct samu *user;
1617 if ((acct_flags != 0)
1618 && ((acct_flags & pwd->acct_ctrl) == 0)) {
1619 continue;
1622 user = samu_new(talloc_tos());
1623 if (user == NULL) {
1624 DEBUG(0, ("samu_new failed\n"));
1625 break;
1628 if (!build_sam_account(smbpasswd_state, user, pwd)) {
1629 /* Already got debug msgs... */
1630 break;
1633 ZERO_STRUCT(entry);
1635 entry.acct_flags = pdb_get_acct_ctrl(user);
1636 sid_peek_rid(pdb_get_user_sid(user), &entry.rid);
1637 entry.account_name = talloc_strdup(
1638 search_state, pdb_get_username(user));
1639 entry.fullname = talloc_strdup(
1640 search_state, pdb_get_fullname(user));
1641 entry.description = talloc_strdup(
1642 search_state, pdb_get_acct_desc(user));
1644 TALLOC_FREE(user);
1646 if ((entry.account_name == NULL) || (entry.fullname == NULL)
1647 || (entry.description == NULL)) {
1648 DEBUG(0, ("talloc_strdup failed\n"));
1649 break;
1652 ADD_TO_LARGE_ARRAY(search_state, struct samr_displayentry,
1653 entry, &search_state->entries,
1654 &search_state->num_entries,
1655 &search_state->array_size);
1658 endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1660 search->private_data = search_state;
1661 search->next_entry = smbpasswd_search_next_entry;
1662 search->search_end = smbpasswd_search_end;
1664 return true;
1667 static NTSTATUS pdb_init_smbpasswd( struct pdb_methods **pdb_method, const char *location )
1669 NTSTATUS nt_status;
1670 struct smbpasswd_privates *privates;
1672 if ( !NT_STATUS_IS_OK(nt_status = make_pdb_method( pdb_method )) ) {
1673 return nt_status;
1676 (*pdb_method)->name = "smbpasswd";
1678 (*pdb_method)->getsampwnam = smbpasswd_getsampwnam;
1679 (*pdb_method)->getsampwsid = smbpasswd_getsampwsid;
1680 (*pdb_method)->add_sam_account = smbpasswd_add_sam_account;
1681 (*pdb_method)->update_sam_account = smbpasswd_update_sam_account;
1682 (*pdb_method)->delete_sam_account = smbpasswd_delete_sam_account;
1683 (*pdb_method)->rename_sam_account = smbpasswd_rename_sam_account;
1684 (*pdb_method)->search_users = smbpasswd_search_users;
1686 (*pdb_method)->capabilities = smbpasswd_capabilities;
1688 /* Setup private data and free function */
1690 if ( !(privates = TALLOC_ZERO_P( *pdb_method, struct smbpasswd_privates )) ) {
1691 DEBUG(0, ("talloc() failed for smbpasswd private_data!\n"));
1692 return NT_STATUS_NO_MEMORY;
1695 /* Store some config details */
1697 if (location) {
1698 privates->smbpasswd_file = talloc_strdup(*pdb_method, location);
1699 } else {
1700 privates->smbpasswd_file = talloc_strdup(*pdb_method, lp_smb_passwd_file());
1703 if (!privates->smbpasswd_file) {
1704 DEBUG(0, ("talloc_strdp() failed for storing smbpasswd location!\n"));
1705 return NT_STATUS_NO_MEMORY;
1708 (*pdb_method)->private_data = privates;
1710 (*pdb_method)->free_private_data = free_private_data;
1712 return NT_STATUS_OK;
1715 NTSTATUS pdb_smbpasswd_init(void)
1717 return smb_register_passdb(PASSDB_INTERFACE_VERSION, "smbpasswd", pdb_init_smbpasswd);