Add test that detects problems in the SMB signing code when run through cli_write...
[Samba.git] / source3 / passdb / pdb_smbpasswd.c
blob8d6ac318cb751ee3344166e921d2c42434d000c3
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"
25 #include "../libcli/security/security.h"
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_PASSDB
30 /*
31 smb_passwd is analogous to sam_passwd used everywhere
32 else. However, smb_passwd is limited to the information
33 stored by an smbpasswd entry
36 struct smb_passwd
38 uint32 smb_userid; /* this is actually the unix uid_t */
39 const char *smb_name; /* username string */
41 const unsigned char *smb_passwd; /* Null if no password */
42 const unsigned char *smb_nt_passwd; /* Null if no password */
44 uint16_t acct_ctrl; /* account info (ACB_xxxx bit-mask) */
45 time_t pass_last_set_time; /* password last set time */
48 struct smbpasswd_privates
50 /* used for maintain locks on the smbpasswd file */
51 int pw_file_lock_depth;
53 /* Global File pointer */
54 FILE *pw_file;
56 /* formerly static variables */
57 struct smb_passwd pw_buf;
58 fstring user_name;
59 unsigned char smbpwd[16];
60 unsigned char smbntpwd[16];
62 /* retrive-once info */
63 const char *smbpasswd_file;
66 enum pwf_access_type { PWF_READ, PWF_UPDATE, PWF_CREATE };
68 static SIG_ATOMIC_T gotalarm;
70 /***************************************************************
71 Signal function to tell us we timed out.
72 ****************************************************************/
74 static void gotalarm_sig(int signum)
76 gotalarm = 1;
79 /***************************************************************
80 Lock or unlock a fd for a known lock type. Abandon after waitsecs
81 seconds.
82 ****************************************************************/
84 static bool do_file_lock(int fd, int waitsecs, int type)
86 SMB_STRUCT_FLOCK lock;
87 int ret;
88 void (*oldsig_handler)(int);
90 gotalarm = 0;
91 oldsig_handler = CatchSignal(SIGALRM, gotalarm_sig);
93 lock.l_type = type;
94 lock.l_whence = SEEK_SET;
95 lock.l_start = 0;
96 lock.l_len = 1;
97 lock.l_pid = 0;
99 alarm(waitsecs);
100 /* Note we must *NOT* use sys_fcntl here ! JRA */
101 ret = fcntl(fd, SMB_F_SETLKW, &lock);
102 alarm(0);
103 CatchSignal(SIGALRM, oldsig_handler);
105 if (gotalarm && ret == -1) {
106 DEBUG(0, ("do_file_lock: failed to %s file.\n",
107 type == F_UNLCK ? "unlock" : "lock"));
108 return False;
111 return (ret == 0);
114 /***************************************************************
115 Lock an fd. Abandon after waitsecs seconds.
116 ****************************************************************/
118 static bool pw_file_lock(int fd, int type, int secs, int *plock_depth)
120 if (fd < 0) {
121 return False;
124 if(*plock_depth == 0) {
125 if (!do_file_lock(fd, secs, type)) {
126 DEBUG(10,("pw_file_lock: locking file failed, error = %s.\n",
127 strerror(errno)));
128 return False;
132 (*plock_depth)++;
134 return True;
137 /***************************************************************
138 Unlock an fd. Abandon after waitsecs seconds.
139 ****************************************************************/
141 static bool pw_file_unlock(int fd, int *plock_depth)
143 bool ret=True;
145 if (fd == 0 || *plock_depth == 0) {
146 return True;
149 if(*plock_depth == 1) {
150 ret = do_file_lock(fd, 5, F_UNLCK);
153 if (*plock_depth > 0) {
154 (*plock_depth)--;
157 if(!ret) {
158 DEBUG(10,("pw_file_unlock: unlocking file failed, error = %s.\n",
159 strerror(errno)));
161 return ret;
164 /**************************************************************
165 Intialize a smb_passwd struct
166 *************************************************************/
168 static void pdb_init_smb(struct smb_passwd *user)
170 if (user == NULL)
171 return;
172 ZERO_STRUCTP (user);
174 user->pass_last_set_time = (time_t)0;
177 /***************************************************************
178 Internal fn to enumerate the smbpasswd list. Returns a void pointer
179 to ensure no modification outside this module. Checks for atomic
180 rename of smbpasswd file on update or create once the lock has
181 been granted to prevent race conditions. JRA.
182 ****************************************************************/
184 static FILE *startsmbfilepwent(const char *pfile, enum pwf_access_type type, int *lock_depth)
186 FILE *fp = NULL;
187 const char *open_mode = NULL;
188 int race_loop = 0;
189 int lock_type = F_RDLCK;
191 if (!*pfile) {
192 DEBUG(0, ("startsmbfilepwent: No SMB password file set\n"));
193 return (NULL);
196 switch(type) {
197 case PWF_READ:
198 open_mode = "rb";
199 lock_type = F_RDLCK;
200 break;
201 case PWF_UPDATE:
202 open_mode = "r+b";
203 lock_type = F_WRLCK;
204 break;
205 case PWF_CREATE:
207 * Ensure atomic file creation.
210 int i, fd = -1;
212 for(i = 0; i < 5; i++) {
213 if((fd = sys_open(pfile, O_CREAT|O_TRUNC|O_EXCL|O_RDWR, 0600))!=-1) {
214 break;
216 sys_usleep(200); /* Spin, spin... */
218 if(fd == -1) {
219 DEBUG(0,("startsmbfilepwent_internal: too many race conditions \
220 creating file %s\n", pfile));
221 return NULL;
223 close(fd);
224 open_mode = "r+b";
225 lock_type = F_WRLCK;
226 break;
230 for(race_loop = 0; race_loop < 5; race_loop++) {
231 DEBUG(10, ("startsmbfilepwent_internal: opening file %s\n", pfile));
233 if((fp = sys_fopen(pfile, open_mode)) == NULL) {
236 * If smbpasswd file doesn't exist, then create new one. This helps to avoid
237 * confusing error msg when adding user account first time.
239 if (errno == ENOENT) {
240 if ((fp = sys_fopen(pfile, "a+")) != NULL) {
241 DEBUG(0, ("startsmbfilepwent_internal: file %s did not \
242 exist. File successfully created.\n", pfile));
243 } else {
244 DEBUG(0, ("startsmbfilepwent_internal: file %s did not \
245 exist. Couldn't create new one. Error was: %s",
246 pfile, strerror(errno)));
247 return NULL;
249 } else {
250 DEBUG(0, ("startsmbfilepwent_internal: unable to open file %s. \
251 Error was: %s\n", pfile, strerror(errno)));
252 return NULL;
256 if (!pw_file_lock(fileno(fp), lock_type, 5, lock_depth)) {
257 DEBUG(0, ("startsmbfilepwent_internal: unable to lock file %s. \
258 Error was %s\n", pfile, strerror(errno) ));
259 fclose(fp);
260 return NULL;
264 * Only check for replacement races on update or create.
265 * For read we don't mind if the data is one record out of date.
268 if(type == PWF_READ) {
269 break;
270 } else {
271 SMB_STRUCT_STAT sbuf1, sbuf2;
274 * Avoid the potential race condition between the open and the lock
275 * by doing a stat on the filename and an fstat on the fd. If the
276 * two inodes differ then someone did a rename between the open and
277 * the lock. Back off and try the open again. Only do this 5 times to
278 * prevent infinate loops. JRA.
281 if (sys_stat(pfile, &sbuf1, false) != 0) {
282 DEBUG(0, ("startsmbfilepwent_internal: unable to stat file %s. \
283 Error was %s\n", pfile, strerror(errno)));
284 pw_file_unlock(fileno(fp), lock_depth);
285 fclose(fp);
286 return NULL;
289 if (sys_fstat(fileno(fp), &sbuf2, false) != 0) {
290 DEBUG(0, ("startsmbfilepwent_internal: unable to fstat file %s. \
291 Error was %s\n", pfile, strerror(errno)));
292 pw_file_unlock(fileno(fp), lock_depth);
293 fclose(fp);
294 return NULL;
297 if( sbuf1.st_ex_ino == sbuf2.st_ex_ino) {
298 /* No race. */
299 break;
303 * Race occurred - back off and try again...
306 pw_file_unlock(fileno(fp), lock_depth);
307 fclose(fp);
311 if(race_loop == 5) {
312 DEBUG(0, ("startsmbfilepwent_internal: too many race conditions opening file %s\n", pfile));
313 return NULL;
316 /* Set a buffer to do more efficient reads */
317 setvbuf(fp, (char *)NULL, _IOFBF, 1024);
319 /* Make sure it is only rw by the owner */
320 #ifdef HAVE_FCHMOD
321 if(fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) {
322 #else
323 if(chmod(pfile, S_IRUSR|S_IWUSR) == -1) {
324 #endif
325 DEBUG(0, ("startsmbfilepwent_internal: failed to set 0600 permissions on password file %s. \
326 Error was %s\n.", pfile, strerror(errno) ));
327 pw_file_unlock(fileno(fp), lock_depth);
328 fclose(fp);
329 return NULL;
332 /* We have a lock on the file. */
333 return fp;
336 /***************************************************************
337 End enumeration of the smbpasswd list.
338 ****************************************************************/
340 static void endsmbfilepwent(FILE *fp, int *lock_depth)
342 if (!fp) {
343 return;
346 pw_file_unlock(fileno(fp), lock_depth);
347 fclose(fp);
348 DEBUG(7, ("endsmbfilepwent_internal: closed password file.\n"));
351 /*************************************************************************
352 Routine to return the next entry in the smbpasswd list.
353 *************************************************************************/
355 static struct smb_passwd *getsmbfilepwent(struct smbpasswd_privates *smbpasswd_state, FILE *fp)
357 /* Static buffers we will return. */
358 struct smb_passwd *pw_buf = &smbpasswd_state->pw_buf;
359 char *user_name = smbpasswd_state->user_name;
360 unsigned char *smbpwd = smbpasswd_state->smbpwd;
361 unsigned char *smbntpwd = smbpasswd_state->smbntpwd;
362 char linebuf[256];
363 int c;
364 unsigned char *p;
365 long uidval;
366 size_t linebuf_len;
367 char *status;
369 if(fp == NULL) {
370 DEBUG(0,("getsmbfilepwent: Bad password file pointer.\n"));
371 return NULL;
374 pdb_init_smb(pw_buf);
375 pw_buf->acct_ctrl = ACB_NORMAL;
378 * Scan the file, a line at a time and check if the name matches.
380 status = linebuf;
381 while (status && !feof(fp)) {
382 linebuf[0] = '\0';
384 status = fgets(linebuf, 256, fp);
385 if (status == NULL && ferror(fp)) {
386 return NULL;
390 * Check if the string is terminated with a newline - if not
391 * then we must keep reading and discard until we get one.
393 if ((linebuf_len = strlen(linebuf)) == 0) {
394 continue;
397 if (linebuf[linebuf_len - 1] != '\n') {
398 c = '\0';
399 while (!ferror(fp) && !feof(fp)) {
400 c = fgetc(fp);
401 if (c == '\n') {
402 break;
405 } else {
406 linebuf[linebuf_len - 1] = '\0';
409 #ifdef DEBUG_PASSWORD
410 DEBUG(100, ("getsmbfilepwent: got line |%s|\n", linebuf));
411 #endif
412 if ((linebuf[0] == 0) && feof(fp)) {
413 DEBUG(4, ("getsmbfilepwent: end of file reached\n"));
414 break;
418 * The line we have should be of the form :-
420 * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently
421 * ignored....
423 * or,
425 * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored....
427 * if Windows NT compatible passwords are also present.
428 * [Account type] is an ascii encoding of the type of account.
429 * LCT-(8 hex digits) is the time_t value of the last change time.
432 if (linebuf[0] == '#' || linebuf[0] == '\0') {
433 DEBUG(6, ("getsmbfilepwent: skipping comment or blank line\n"));
434 continue;
436 p = (unsigned char *) strchr_m(linebuf, ':');
437 if (p == NULL) {
438 DEBUG(0, ("getsmbfilepwent: malformed password entry (no :)\n"));
439 continue;
442 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
443 user_name[PTR_DIFF(p, linebuf)] = '\0';
445 /* Get smb uid. */
447 p++; /* Go past ':' */
449 if(*p == '-') {
450 DEBUG(0, ("getsmbfilepwent: user name %s has a negative uid.\n", user_name));
451 continue;
454 if (!isdigit(*p)) {
455 DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (uid not number)\n",
456 user_name));
457 continue;
460 uidval = atoi((char *) p);
462 while (*p && isdigit(*p)) {
463 p++;
466 if (*p != ':') {
467 DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (no : after uid)\n",
468 user_name));
469 continue;
472 pw_buf->smb_name = user_name;
473 pw_buf->smb_userid = uidval;
476 * Now get the password value - this should be 32 hex digits
477 * which are the ascii representations of a 16 byte string.
478 * Get two at a time and put them into the password.
481 /* Skip the ':' */
482 p++;
484 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
485 DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (passwd too short)\n",
486 user_name ));
487 continue;
490 if (p[32] != ':') {
491 DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (no terminating :)\n",
492 user_name));
493 continue;
496 if (strnequal((char *) p, "NO PASSWORD", 11)) {
497 pw_buf->smb_passwd = NULL;
498 pw_buf->acct_ctrl |= ACB_PWNOTREQ;
499 } else {
500 if (*p == '*' || *p == 'X') {
501 /* NULL LM password */
502 pw_buf->smb_passwd = NULL;
503 DEBUG(10, ("getsmbfilepwent: LM password for user %s invalidated\n", user_name));
504 } else if (pdb_gethexpwd((char *)p, smbpwd)) {
505 pw_buf->smb_passwd = smbpwd;
506 } else {
507 pw_buf->smb_passwd = NULL;
508 DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry for user %s \
509 (non hex chars)\n", user_name));
514 * Now check if the NT compatible password is
515 * available.
517 pw_buf->smb_nt_passwd = NULL;
518 p += 33; /* Move to the first character of the line after the lanman password. */
519 if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
520 if (*p != '*' && *p != 'X') {
521 if(pdb_gethexpwd((char *)p,smbntpwd)) {
522 pw_buf->smb_nt_passwd = smbntpwd;
525 p += 33; /* Move to the first character of the line after the NT password. */
528 DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n",
529 user_name, uidval));
531 if (*p == '[') {
532 unsigned char *end_p = (unsigned char *)strchr_m((char *)p, ']');
533 pw_buf->acct_ctrl = pdb_decode_acct_ctrl((char*)p);
535 /* Must have some account type set. */
536 if(pw_buf->acct_ctrl == 0) {
537 pw_buf->acct_ctrl = ACB_NORMAL;
540 /* Now try and get the last change time. */
541 if(end_p) {
542 p = end_p + 1;
544 if(*p == ':') {
545 p++;
546 if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) {
547 int i;
548 p += 4;
549 for(i = 0; i < 8; i++) {
550 if(p[i] == '\0' || !isxdigit(p[i])) {
551 break;
554 if(i == 8) {
556 * p points at 8 characters of hex digits -
557 * read into a time_t as the seconds since
558 * 1970 that the password was last changed.
560 pw_buf->pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
564 } else {
565 /* 'Old' style file. Fake up based on user name. */
567 * Currently trust accounts are kept in the same
568 * password file as 'normal accounts'. If this changes
569 * we will have to fix this code. JRA.
571 if(pw_buf->smb_name[strlen(pw_buf->smb_name) - 1] == '$') {
572 pw_buf->acct_ctrl &= ~ACB_NORMAL;
573 pw_buf->acct_ctrl |= ACB_WSTRUST;
577 return pw_buf;
580 DEBUG(5,("getsmbfilepwent: end of file reached.\n"));
581 return NULL;
584 /************************************************************************
585 Create a new smbpasswd entry - malloced space returned.
586 *************************************************************************/
588 static char *format_new_smbpasswd_entry(const struct smb_passwd *newpwd)
590 int new_entry_length;
591 char *new_entry;
592 char *p;
594 new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 +
595 NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2;
597 if((new_entry = (char *)SMB_MALLOC( new_entry_length )) == NULL) {
598 DEBUG(0, ("format_new_smbpasswd_entry: Malloc failed adding entry for user %s.\n",
599 newpwd->smb_name ));
600 return NULL;
603 slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid);
605 p = new_entry+strlen(new_entry);
606 pdb_sethexpwd(p, newpwd->smb_passwd, newpwd->acct_ctrl);
607 p+=strlen(p);
608 *p = ':';
609 p++;
611 pdb_sethexpwd(p, newpwd->smb_nt_passwd, newpwd->acct_ctrl);
612 p+=strlen(p);
613 *p = ':';
614 p++;
616 /* Add the account encoding and the last change time. */
617 slprintf((char *)p, new_entry_length - 1 - (p - new_entry), "%s:LCT-%08X:\n",
618 pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN),
619 (uint32_t)newpwd->pass_last_set_time);
621 return new_entry;
624 /************************************************************************
625 Routine to add an entry to the smbpasswd file.
626 *************************************************************************/
628 static NTSTATUS add_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state,
629 struct smb_passwd *newpwd)
631 const char *pfile = smbpasswd_state->smbpasswd_file;
632 struct smb_passwd *pwd = NULL;
633 FILE *fp = NULL;
634 int wr_len;
635 int fd;
636 size_t new_entry_length;
637 char *new_entry;
638 SMB_OFF_T offpos;
640 /* Open the smbpassword file - for update. */
641 fp = startsmbfilepwent(pfile, PWF_UPDATE, &smbpasswd_state->pw_file_lock_depth);
643 if (fp == NULL && errno == ENOENT) {
644 /* Try again - create. */
645 fp = startsmbfilepwent(pfile, PWF_CREATE, &smbpasswd_state->pw_file_lock_depth);
648 if (fp == NULL) {
649 DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n"));
650 return map_nt_error_from_unix(errno);
654 * Scan the file, a line at a time and check if the name matches.
657 while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
658 if (strequal(newpwd->smb_name, pwd->smb_name)) {
659 DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name));
660 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
661 return NT_STATUS_USER_EXISTS;
665 /* Ok - entry doesn't exist. We can add it */
667 /* Create a new smb passwd entry and set it to the given password. */
669 * The add user write needs to be atomic - so get the fd from
670 * the fp and do a raw write() call.
672 fd = fileno(fp);
674 if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1) {
675 NTSTATUS result = map_nt_error_from_unix(errno);
676 DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \
677 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
678 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
679 return result;
682 if((new_entry = format_new_smbpasswd_entry(newpwd)) == NULL) {
683 DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
684 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
685 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
686 return NT_STATUS_NO_MEMORY;
689 new_entry_length = strlen(new_entry);
691 #ifdef DEBUG_PASSWORD
692 DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d made line |%s|",
693 fd, (int)new_entry_length, new_entry));
694 #endif
696 if ((wr_len = write(fd, new_entry, new_entry_length)) != new_entry_length) {
697 NTSTATUS result = map_nt_error_from_unix(errno);
698 DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \
699 Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
701 /* Remove the entry we just wrote. */
702 if(sys_ftruncate(fd, offpos) == -1) {
703 DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \
704 Error was %s. Password file may be corrupt ! Please examine by hand !\n",
705 newpwd->smb_name, strerror(errno)));
708 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
709 free(new_entry);
710 return result;
713 free(new_entry);
714 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
715 return NT_STATUS_OK;
718 /************************************************************************
719 Routine to search the smbpasswd file for an entry matching the username.
720 and then modify its password entry. We can't use the startsmbpwent()/
721 getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
722 in the actual file to decide how much room we have to write data.
723 override = False, normal
724 override = True, override XXXXXXXX'd out password or NO PASS
725 ************************************************************************/
727 static bool mod_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const struct smb_passwd* pwd)
729 /* Static buffers we will return. */
730 fstring user_name;
732 char *status;
733 char linebuf[256];
734 char readbuf[1024];
735 int c;
736 fstring ascii_p16;
737 fstring encode_bits;
738 unsigned char *p = NULL;
739 size_t linebuf_len = 0;
740 FILE *fp;
741 int lockfd;
742 const char *pfile = smbpasswd_state->smbpasswd_file;
743 bool found_entry = False;
744 bool got_pass_last_set_time = False;
746 SMB_OFF_T pwd_seekpos = 0;
748 int i;
749 int wr_len;
750 int fd;
752 if (!*pfile) {
753 DEBUG(0, ("No SMB password file set\n"));
754 return False;
756 DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
758 fp = sys_fopen(pfile, "r+");
760 if (fp == NULL) {
761 DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
762 return False;
764 /* Set a buffer to do more efficient reads */
765 setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
767 lockfd = fileno(fp);
769 if (!pw_file_lock(lockfd, F_WRLCK, 5, &smbpasswd_state->pw_file_lock_depth)) {
770 DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile));
771 fclose(fp);
772 return False;
775 /* Make sure it is only rw by the owner */
776 chmod(pfile, 0600);
778 /* We have a write lock on the file. */
780 * Scan the file, a line at a time and check if the name matches.
782 status = linebuf;
783 while (status && !feof(fp)) {
784 pwd_seekpos = sys_ftell(fp);
786 linebuf[0] = '\0';
788 status = fgets(linebuf, sizeof(linebuf), fp);
789 if (status == NULL && ferror(fp)) {
790 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
791 fclose(fp);
792 return False;
796 * Check if the string is terminated with a newline - if not
797 * then we must keep reading and discard until we get one.
799 linebuf_len = strlen(linebuf);
800 if (linebuf[linebuf_len - 1] != '\n') {
801 c = '\0';
802 while (!ferror(fp) && !feof(fp)) {
803 c = fgetc(fp);
804 if (c == '\n') {
805 break;
808 } else {
809 linebuf[linebuf_len - 1] = '\0';
812 #ifdef DEBUG_PASSWORD
813 DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
814 #endif
816 if ((linebuf[0] == 0) && feof(fp)) {
817 DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
818 break;
822 * The line we have should be of the form :-
824 * username:uid:[32hex bytes]:....other flags presently
825 * ignored....
827 * or,
829 * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
831 * if Windows NT compatible passwords are also present.
834 if (linebuf[0] == '#' || linebuf[0] == '\0') {
835 DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
836 continue;
839 p = (unsigned char *) strchr_m(linebuf, ':');
841 if (p == NULL) {
842 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
843 continue;
846 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
847 user_name[PTR_DIFF(p, linebuf)] = '\0';
848 if (strequal(user_name, pwd->smb_name)) {
849 found_entry = True;
850 break;
854 if (!found_entry) {
855 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
856 fclose(fp);
858 DEBUG(2, ("Cannot update entry for user %s, as they don't exist in the smbpasswd file!\n",
859 pwd->smb_name));
860 return False;
863 DEBUG(6, ("mod_smbfilepwd_entry: entry exists for user %s\n", pwd->smb_name));
865 /* User name matches - get uid and password */
866 p++; /* Go past ':' */
868 if (!isdigit(*p)) {
869 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (uid not number)\n",
870 pwd->smb_name));
871 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
872 fclose(fp);
873 return False;
876 while (*p && isdigit(*p)) {
877 p++;
879 if (*p != ':') {
880 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no : after uid)\n",
881 pwd->smb_name));
882 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
883 fclose(fp);
884 return False;
888 * Now get the password value - this should be 32 hex digits
889 * which are the ascii representations of a 16 byte string.
890 * Get two at a time and put them into the password.
892 p++;
894 /* Record exact password position */
895 pwd_seekpos += PTR_DIFF(p, linebuf);
897 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
898 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (passwd too short)\n",
899 pwd->smb_name));
900 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
901 fclose(fp);
902 return (False);
905 if (p[32] != ':') {
906 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no terminating :)\n",
907 pwd->smb_name));
908 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
909 fclose(fp);
910 return False;
913 /* Now check if the NT compatible password is available. */
914 p += 33; /* Move to the first character of the line after the lanman password. */
915 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
916 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (passwd too short)\n",
917 pwd->smb_name));
918 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
919 fclose(fp);
920 return (False);
923 if (p[32] != ':') {
924 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no terminating :)\n",
925 pwd->smb_name));
926 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
927 fclose(fp);
928 return False;
932 * Now check if the account info and the password last
933 * change time is available.
935 p += 33; /* Move to the first character of the line after the NT password. */
937 if (*p == '[') {
938 i = 0;
939 encode_bits[i++] = *p++;
940 while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']')) {
941 encode_bits[i++] = *p++;
944 encode_bits[i++] = ']';
945 encode_bits[i++] = '\0';
947 if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
949 * We are using a new format, space padded
950 * acct ctrl field. Encode the given acct ctrl
951 * bits into it.
953 fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
954 } else {
955 DEBUG(0,("mod_smbfilepwd_entry: Using old smbpasswd format for user %s. \
956 This is no longer supported.!\n", pwd->smb_name));
957 DEBUG(0,("mod_smbfilepwd_entry: No changes made, failing.!\n"));
958 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
959 fclose(fp);
960 return False;
963 /* Go past the ']' */
964 if(linebuf_len > PTR_DIFF(p, linebuf)) {
965 p++;
968 if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
969 p++;
971 /* We should be pointing at the LCT entry. */
972 if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
973 p += 4;
974 for(i = 0; i < 8; i++) {
975 if(p[i] == '\0' || !isxdigit(p[i])) {
976 break;
979 if(i == 8) {
981 * p points at 8 characters of hex digits -
982 * read into a time_t as the seconds since
983 * 1970 that the password was last changed.
985 got_pass_last_set_time = True;
986 } /* i == 8 */
987 } /* *p && StrnCaseCmp() */
988 } /* p == ':' */
989 } /* p == '[' */
991 /* Entry is correctly formed. */
993 /* Create the 32 byte representation of the new p16 */
994 pdb_sethexpwd(ascii_p16, pwd->smb_passwd, pwd->acct_ctrl);
996 /* Add on the NT md4 hash */
997 ascii_p16[32] = ':';
998 wr_len = 66;
999 pdb_sethexpwd(ascii_p16+33, pwd->smb_nt_passwd, pwd->acct_ctrl);
1000 ascii_p16[65] = ':';
1001 ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
1003 /* Add on the account info bits and the time of last password change. */
1004 if(got_pass_last_set_time) {
1005 slprintf(&ascii_p16[strlen(ascii_p16)],
1006 sizeof(ascii_p16)-(strlen(ascii_p16)+1),
1007 "%s:LCT-%08X:",
1008 encode_bits, (uint32_t)pwd->pass_last_set_time );
1009 wr_len = strlen(ascii_p16);
1012 #ifdef DEBUG_PASSWORD
1013 DEBUG(100,("mod_smbfilepwd_entry: "));
1014 dump_data(100, (uint8 *)ascii_p16, wr_len);
1015 #endif
1017 if(wr_len > sizeof(linebuf)) {
1018 DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
1019 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1020 fclose(fp);
1021 return (False);
1025 * Do an atomic write into the file at the position defined by
1026 * seekpos.
1029 /* The mod user write needs to be atomic - so get the fd from
1030 the fp and do a raw write() call.
1033 fd = fileno(fp);
1035 if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
1036 DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1037 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1038 fclose(fp);
1039 return False;
1042 /* Sanity check - ensure the areas we are writing are framed by ':' */
1043 if (read(fd, linebuf, wr_len+1) != wr_len+1) {
1044 DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
1045 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1046 fclose(fp);
1047 return False;
1050 if ((linebuf[0] != ':') || (linebuf[wr_len] != ':')) {
1051 DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
1052 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1053 fclose(fp);
1054 return False;
1057 if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
1058 DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1059 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1060 fclose(fp);
1061 return False;
1064 if (write(fd, ascii_p16, wr_len) != wr_len) {
1065 DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
1066 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1067 fclose(fp);
1068 return False;
1071 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1072 fclose(fp);
1073 return True;
1076 /************************************************************************
1077 Routine to delete an entry in the smbpasswd file by name.
1078 *************************************************************************/
1080 static bool del_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const char *name)
1082 const char *pfile = smbpasswd_state->smbpasswd_file;
1083 char *pfile2 = NULL;
1084 struct smb_passwd *pwd = NULL;
1085 FILE *fp = NULL;
1086 FILE *fp_write = NULL;
1087 int pfile2_lockdepth = 0;
1089 pfile2 = talloc_asprintf(talloc_tos(),
1090 "%s.%u",
1091 pfile, (unsigned)sys_getpid());
1092 if (!pfile2) {
1093 return false;
1097 * Open the smbpassword file - for update. It needs to be update
1098 * as we need any other processes to wait until we have replaced
1099 * it.
1102 if((fp = startsmbfilepwent(pfile, PWF_UPDATE, &smbpasswd_state->pw_file_lock_depth)) == NULL) {
1103 DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1104 return False;
1108 * Create the replacement password file.
1110 if((fp_write = startsmbfilepwent(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) {
1111 DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1112 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1113 return False;
1117 * Scan the file, a line at a time and check if the name matches.
1120 while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
1121 char *new_entry;
1122 size_t new_entry_length;
1124 if (strequal(name, pwd->smb_name)) {
1125 DEBUG(10, ("del_smbfilepwd_entry: found entry with "
1126 "name %s - deleting it.\n", name));
1127 continue;
1131 * We need to copy the entry out into the second file.
1134 if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL) {
1135 DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \
1136 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1137 unlink(pfile2);
1138 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1139 endsmbfilepwent(fp_write, &pfile2_lockdepth);
1140 return False;
1143 new_entry_length = strlen(new_entry);
1145 if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length) {
1146 DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \
1147 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1148 unlink(pfile2);
1149 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1150 endsmbfilepwent(fp_write, &pfile2_lockdepth);
1151 free(new_entry);
1152 return False;
1155 free(new_entry);
1159 * Ensure pfile2 is flushed before rename.
1162 if(fflush(fp_write) != 0) {
1163 DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno)));
1164 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1165 endsmbfilepwent(fp_write,&pfile2_lockdepth);
1166 return False;
1170 * Do an atomic rename - then release the locks.
1173 if(rename(pfile2,pfile) != 0) {
1174 unlink(pfile2);
1177 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1178 endsmbfilepwent(fp_write,&pfile2_lockdepth);
1179 return True;
1182 /*********************************************************************
1183 Create a smb_passwd struct from a struct samu.
1184 We will not allocate any new memory. The smb_passwd struct
1185 should only stay around as long as the struct samu does.
1186 ********************************************************************/
1188 static bool build_smb_pass (struct smb_passwd *smb_pw, const struct samu *sampass)
1190 uint32_t rid;
1192 if (sampass == NULL)
1193 return False;
1194 ZERO_STRUCTP(smb_pw);
1196 if (!IS_SAM_DEFAULT(sampass, PDB_USERSID)) {
1197 rid = pdb_get_user_rid(sampass);
1199 /* If the user specified a RID, make sure its able to be both stored and retreived */
1200 if (rid == DOMAIN_RID_GUEST) {
1201 struct passwd *passwd = Get_Pwnam_alloc(NULL, lp_guestaccount());
1202 if (!passwd) {
1203 DEBUG(0, ("Could not find guest account via Get_Pwnam_alloc()! (%s)\n", lp_guestaccount()));
1204 return False;
1206 smb_pw->smb_userid=passwd->pw_uid;
1207 TALLOC_FREE(passwd);
1208 } else if (algorithmic_pdb_rid_is_user(rid)) {
1209 smb_pw->smb_userid=algorithmic_pdb_user_rid_to_uid(rid);
1210 } else {
1211 DEBUG(0,("build_sam_pass: Failing attempt to store user with non-uid based user RID. \n"));
1212 return False;
1216 smb_pw->smb_name=(const char*)pdb_get_username(sampass);
1218 smb_pw->smb_passwd=pdb_get_lanman_passwd(sampass);
1219 smb_pw->smb_nt_passwd=pdb_get_nt_passwd(sampass);
1221 smb_pw->acct_ctrl=pdb_get_acct_ctrl(sampass);
1222 smb_pw->pass_last_set_time=pdb_get_pass_last_set_time(sampass);
1224 return True;
1227 /*********************************************************************
1228 Create a struct samu from a smb_passwd struct
1229 ********************************************************************/
1231 static bool build_sam_account(struct smbpasswd_privates *smbpasswd_state,
1232 struct samu *sam_pass, const struct smb_passwd *pw_buf)
1234 struct passwd *pwfile;
1236 if ( !sam_pass ) {
1237 DEBUG(5,("build_sam_account: struct samu is NULL\n"));
1238 return False;
1241 /* verify the user account exists */
1243 if ( !(pwfile = Get_Pwnam_alloc(NULL, pw_buf->smb_name )) ) {
1244 DEBUG(0,("build_sam_account: smbpasswd database is corrupt! username %s with uid "
1245 "%u is not in unix passwd database!\n", pw_buf->smb_name, pw_buf->smb_userid));
1246 return False;
1249 if ( !NT_STATUS_IS_OK( samu_set_unix(sam_pass, pwfile )) )
1250 return False;
1252 TALLOC_FREE(pwfile);
1254 /* set remaining fields */
1256 if (!pdb_set_nt_passwd (sam_pass, pw_buf->smb_nt_passwd, PDB_SET))
1257 return False;
1258 if (!pdb_set_lanman_passwd (sam_pass, pw_buf->smb_passwd, PDB_SET))
1259 return False;
1260 pdb_set_acct_ctrl (sam_pass, pw_buf->acct_ctrl, PDB_SET);
1261 pdb_set_pass_last_set_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
1262 pdb_set_pass_can_change_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
1264 return True;
1267 /*****************************************************************
1268 Functions to be implemented by the new passdb API
1269 ****************************************************************/
1271 /****************************************************************
1272 Search smbpasswd file by iterating over the entries. Do not
1273 call getpwnam() for unix account information until we have found
1274 the correct entry
1275 ***************************************************************/
1277 static NTSTATUS smbpasswd_getsampwnam(struct pdb_methods *my_methods,
1278 struct samu *sam_acct, const char *username)
1280 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1281 struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1282 struct smb_passwd *smb_pw;
1283 FILE *fp = NULL;
1285 DEBUG(10, ("getsampwnam (smbpasswd): search by name: %s\n", username));
1287 /* startsmbfilepwent() is used here as we don't want to lookup
1288 the UNIX account in the local system password file until
1289 we have a match. */
1290 fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1292 if (fp == NULL) {
1293 DEBUG(0, ("Unable to open passdb database.\n"));
1294 return nt_status;
1297 while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL)&& (!strequal(smb_pw->smb_name, username)) )
1298 /* do nothing....another loop */ ;
1300 endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1303 /* did we locate the username in smbpasswd */
1304 if (smb_pw == NULL)
1305 return nt_status;
1307 DEBUG(10, ("getsampwnam (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1309 if (!sam_acct) {
1310 DEBUG(10,("getsampwnam (smbpasswd): struct samu is NULL\n"));
1311 return nt_status;
1314 /* now build the struct samu */
1315 if (!build_sam_account(smbpasswd_state, sam_acct, smb_pw))
1316 return nt_status;
1318 /* success */
1319 return NT_STATUS_OK;
1322 static NTSTATUS smbpasswd_getsampwsid(struct pdb_methods *my_methods, struct samu *sam_acct, const struct dom_sid *sid)
1324 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1325 struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1326 struct smb_passwd *smb_pw;
1327 FILE *fp = NULL;
1328 uint32_t rid;
1330 DEBUG(10, ("smbpasswd_getsampwrid: search by sid: %s\n",
1331 sid_string_dbg(sid)));
1333 if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
1334 return NT_STATUS_UNSUCCESSFUL;
1336 /* More special case 'guest account' hacks... */
1337 if (rid == DOMAIN_RID_GUEST) {
1338 const char *guest_account = lp_guestaccount();
1339 if (!(guest_account && *guest_account)) {
1340 DEBUG(1, ("Guest account not specfied!\n"));
1341 return nt_status;
1343 return smbpasswd_getsampwnam(my_methods, sam_acct, guest_account);
1346 /* Open the sam password file - not for update. */
1347 fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1349 if (fp == NULL) {
1350 DEBUG(0, ("Unable to open passdb database.\n"));
1351 return nt_status;
1354 while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL) && (algorithmic_pdb_uid_to_user_rid(smb_pw->smb_userid) != rid) )
1355 /* do nothing */ ;
1357 endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1360 /* did we locate the username in smbpasswd */
1361 if (smb_pw == NULL)
1362 return nt_status;
1364 DEBUG(10, ("getsampwrid (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1366 if (!sam_acct) {
1367 DEBUG(10,("getsampwrid: (smbpasswd) struct samu is NULL\n"));
1368 return nt_status;
1371 /* now build the struct samu */
1372 if (!build_sam_account (smbpasswd_state, sam_acct, smb_pw))
1373 return nt_status;
1375 /* build_sam_account might change the SID on us, if the name was for the guest account */
1376 if (NT_STATUS_IS_OK(nt_status) && !dom_sid_equal(pdb_get_user_sid(sam_acct), sid)) {
1377 DEBUG(1, ("looking for user with sid %s instead returned %s "
1378 "for account %s!?!\n", sid_string_dbg(sid),
1379 sid_string_dbg(pdb_get_user_sid(sam_acct)),
1380 pdb_get_username(sam_acct)));
1381 return NT_STATUS_NO_SUCH_USER;
1384 /* success */
1385 return NT_STATUS_OK;
1388 static NTSTATUS smbpasswd_add_sam_account(struct pdb_methods *my_methods, struct samu *sampass)
1390 struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1391 struct smb_passwd smb_pw;
1393 /* convert the struct samu */
1394 if (!build_smb_pass(&smb_pw, sampass)) {
1395 return NT_STATUS_UNSUCCESSFUL;
1398 /* add the entry */
1399 return add_smbfilepwd_entry(smbpasswd_state, &smb_pw);
1402 static NTSTATUS smbpasswd_update_sam_account(struct pdb_methods *my_methods, struct samu *sampass)
1404 struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1405 struct smb_passwd smb_pw;
1407 /* convert the struct samu */
1408 if (!build_smb_pass(&smb_pw, sampass)) {
1409 DEBUG(0, ("smbpasswd_update_sam_account: build_smb_pass failed!\n"));
1410 return NT_STATUS_UNSUCCESSFUL;
1413 /* update the entry */
1414 if(!mod_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
1415 DEBUG(0, ("smbpasswd_update_sam_account: mod_smbfilepwd_entry failed!\n"));
1416 return NT_STATUS_UNSUCCESSFUL;
1419 return NT_STATUS_OK;
1422 static NTSTATUS smbpasswd_delete_sam_account (struct pdb_methods *my_methods, struct samu *sampass)
1424 struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1426 const char *username = pdb_get_username(sampass);
1428 if (del_smbfilepwd_entry(smbpasswd_state, username))
1429 return NT_STATUS_OK;
1431 return NT_STATUS_UNSUCCESSFUL;
1434 static NTSTATUS smbpasswd_rename_sam_account (struct pdb_methods *my_methods,
1435 struct samu *old_acct,
1436 const char *newname)
1438 char *rename_script = NULL;
1439 struct samu *new_acct = NULL;
1440 bool interim_account = False;
1441 TALLOC_CTX *ctx = talloc_tos();
1442 NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
1444 if (!*(lp_renameuser_script()))
1445 goto done;
1447 if ( !(new_acct = samu_new( NULL )) ) {
1448 return NT_STATUS_NO_MEMORY;
1451 if ( !pdb_copy_sam_account( new_acct, old_acct )
1452 || !pdb_set_username(new_acct, newname, PDB_CHANGED))
1454 goto done;
1457 ret = smbpasswd_add_sam_account(my_methods, new_acct);
1458 if (!NT_STATUS_IS_OK(ret))
1459 goto done;
1461 interim_account = True;
1463 /* rename the posix user */
1464 rename_script = talloc_strdup(ctx,
1465 lp_renameuser_script());
1466 if (!rename_script) {
1467 ret = NT_STATUS_NO_MEMORY;
1468 goto done;
1471 if (*rename_script) {
1472 int rename_ret;
1474 rename_script = talloc_string_sub2(ctx,
1475 rename_script,
1476 "%unew",
1477 newname,
1478 true,
1479 false,
1480 true);
1481 if (!rename_script) {
1482 ret = NT_STATUS_NO_MEMORY;
1483 goto done;
1485 rename_script = talloc_string_sub2(ctx,
1486 rename_script,
1487 "%uold",
1488 pdb_get_username(old_acct),
1489 true,
1490 false,
1491 true);
1492 if (!rename_script) {
1493 ret = NT_STATUS_NO_MEMORY;
1494 goto done;
1497 rename_ret = smbrun(rename_script, NULL);
1499 DEBUG(rename_ret ? 0 : 3,("Running the command `%s' gave %d\n", rename_script, rename_ret));
1501 if (rename_ret == 0) {
1502 smb_nscd_flush_user_cache();
1505 if (rename_ret)
1506 goto done;
1507 } else {
1508 goto done;
1511 smbpasswd_delete_sam_account(my_methods, old_acct);
1512 interim_account = False;
1514 done:
1515 /* cleanup */
1516 if (interim_account)
1517 smbpasswd_delete_sam_account(my_methods, new_acct);
1519 if (new_acct)
1520 TALLOC_FREE(new_acct);
1522 return (ret);
1525 static uint32_t smbpasswd_capabilities(struct pdb_methods *methods)
1527 return 0;
1530 static void free_private_data(void **vp)
1532 struct smbpasswd_privates **privates = (struct smbpasswd_privates**)vp;
1534 endsmbfilepwent((*privates)->pw_file, &((*privates)->pw_file_lock_depth));
1536 *privates = NULL;
1537 /* No need to free any further, as it is talloc()ed */
1540 struct smbpasswd_search_state {
1541 uint32_t acct_flags;
1543 struct samr_displayentry *entries;
1544 uint32_t num_entries;
1545 ssize_t array_size;
1546 uint32_t current;
1549 static void smbpasswd_search_end(struct pdb_search *search)
1551 struct smbpasswd_search_state *state = talloc_get_type_abort(
1552 search->private_data, struct smbpasswd_search_state);
1553 TALLOC_FREE(state);
1556 static bool smbpasswd_search_next_entry(struct pdb_search *search,
1557 struct samr_displayentry *entry)
1559 struct smbpasswd_search_state *state = talloc_get_type_abort(
1560 search->private_data, struct smbpasswd_search_state);
1562 if (state->current == state->num_entries) {
1563 return false;
1566 entry->idx = state->entries[state->current].idx;
1567 entry->rid = state->entries[state->current].rid;
1568 entry->acct_flags = state->entries[state->current].acct_flags;
1570 entry->account_name = talloc_strdup(
1571 search, state->entries[state->current].account_name);
1572 entry->fullname = talloc_strdup(
1573 search, state->entries[state->current].fullname);
1574 entry->description = talloc_strdup(
1575 search, state->entries[state->current].description);
1577 if ((entry->account_name == NULL) || (entry->fullname == NULL)
1578 || (entry->description == NULL)) {
1579 DEBUG(0, ("talloc_strdup failed\n"));
1580 return false;
1583 state->current += 1;
1584 return true;
1587 static bool smbpasswd_search_users(struct pdb_methods *methods,
1588 struct pdb_search *search,
1589 uint32_t acct_flags)
1591 struct smbpasswd_privates *smbpasswd_state =
1592 (struct smbpasswd_privates*)methods->private_data;
1594 struct smbpasswd_search_state *search_state;
1595 struct smb_passwd *pwd;
1596 FILE *fp;
1598 search_state = talloc_zero(search, struct smbpasswd_search_state);
1599 if (search_state == NULL) {
1600 DEBUG(0, ("talloc failed\n"));
1601 return false;
1603 search_state->acct_flags = acct_flags;
1605 fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ,
1606 &smbpasswd_state->pw_file_lock_depth);
1608 if (fp == NULL) {
1609 DEBUG(10, ("Unable to open smbpasswd file.\n"));
1610 TALLOC_FREE(search_state);
1611 return false;
1614 while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
1615 struct samr_displayentry entry;
1616 struct samu *user;
1618 if ((acct_flags != 0)
1619 && ((acct_flags & pwd->acct_ctrl) == 0)) {
1620 continue;
1623 user = samu_new(talloc_tos());
1624 if (user == NULL) {
1625 DEBUG(0, ("samu_new failed\n"));
1626 break;
1629 if (!build_sam_account(smbpasswd_state, user, pwd)) {
1630 /* Already got debug msgs... */
1631 break;
1634 ZERO_STRUCT(entry);
1636 entry.acct_flags = pdb_get_acct_ctrl(user);
1637 sid_peek_rid(pdb_get_user_sid(user), &entry.rid);
1638 entry.account_name = talloc_strdup(
1639 search_state, pdb_get_username(user));
1640 entry.fullname = talloc_strdup(
1641 search_state, pdb_get_fullname(user));
1642 entry.description = talloc_strdup(
1643 search_state, pdb_get_acct_desc(user));
1645 TALLOC_FREE(user);
1647 if ((entry.account_name == NULL) || (entry.fullname == NULL)
1648 || (entry.description == NULL)) {
1649 DEBUG(0, ("talloc_strdup failed\n"));
1650 break;
1653 ADD_TO_LARGE_ARRAY(search_state, struct samr_displayentry,
1654 entry, &search_state->entries,
1655 &search_state->num_entries,
1656 &search_state->array_size);
1659 endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1661 search->private_data = search_state;
1662 search->next_entry = smbpasswd_search_next_entry;
1663 search->search_end = smbpasswd_search_end;
1665 return true;
1668 static NTSTATUS pdb_init_smbpasswd( struct pdb_methods **pdb_method, const char *location )
1670 NTSTATUS nt_status;
1671 struct smbpasswd_privates *privates;
1673 if ( !NT_STATUS_IS_OK(nt_status = make_pdb_method( pdb_method )) ) {
1674 return nt_status;
1677 (*pdb_method)->name = "smbpasswd";
1679 (*pdb_method)->getsampwnam = smbpasswd_getsampwnam;
1680 (*pdb_method)->getsampwsid = smbpasswd_getsampwsid;
1681 (*pdb_method)->add_sam_account = smbpasswd_add_sam_account;
1682 (*pdb_method)->update_sam_account = smbpasswd_update_sam_account;
1683 (*pdb_method)->delete_sam_account = smbpasswd_delete_sam_account;
1684 (*pdb_method)->rename_sam_account = smbpasswd_rename_sam_account;
1685 (*pdb_method)->search_users = smbpasswd_search_users;
1687 (*pdb_method)->capabilities = smbpasswd_capabilities;
1689 /* Setup private data and free function */
1691 if ( !(privates = TALLOC_ZERO_P( *pdb_method, struct smbpasswd_privates )) ) {
1692 DEBUG(0, ("talloc() failed for smbpasswd private_data!\n"));
1693 return NT_STATUS_NO_MEMORY;
1696 /* Store some config details */
1698 if (location) {
1699 privates->smbpasswd_file = talloc_strdup(*pdb_method, location);
1700 } else {
1701 privates->smbpasswd_file = talloc_strdup(*pdb_method, lp_smb_passwd_file());
1704 if (!privates->smbpasswd_file) {
1705 DEBUG(0, ("talloc_strdp() failed for storing smbpasswd location!\n"));
1706 return NT_STATUS_NO_MEMORY;
1709 (*pdb_method)->private_data = privates;
1711 (*pdb_method)->free_private_data = free_private_data;
1713 return NT_STATUS_OK;
1716 NTSTATUS pdb_smbpasswd_init(void)
1718 return smb_register_passdb(PASSDB_INTERFACE_VERSION, "smbpasswd", pdb_init_smbpasswd);