2 * Unix SMB/Netbios implementation.
3 * Version 1.9. 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
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License as published by the Free
10 * Software Foundation; either version 2 of the License, or (at your option)
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 * You should have received a copy of the GNU General Public License along with
19 * this program; if not, write to the Free Software Foundation, Inc., 675
20 * Mass Ave, Cambridge, MA 02139, USA.
25 #ifdef WITH_SMBPASSWD_SAM
29 smb_passwd is analogous to sam_passwd used everywhere
30 else. However, smb_passwd is limited to the information
31 stored by an smbpasswd entry
36 uid_t smb_userid; /* this is actually the unix uid_t */
37 char *smb_name; /* username string */
39 unsigned char *smb_passwd; /* Null if no password */
40 unsigned char *smb_nt_passwd; /* Null if no password */
42 uint16 acct_ctrl; /* account info (ACB_xxxx bit-mask) */
43 time_t pass_last_set_time; /* password last set time */
47 extern pstring samlogon_user;
48 extern BOOL sam_logon_in_ssb;
49 extern struct passdb_ops pdb_ops;
51 /* used for maintain locks on the smbpasswd file */
52 static int pw_file_lock_depth;
53 static void *global_vp;
56 enum pwf_access_type { PWF_READ, PWF_UPDATE, PWF_CREATE };
58 /***************************************************************
59 Lock an fd. Abandon after waitsecs seconds.
60 ****************************************************************/
62 static BOOL pw_file_lock(int fd, int type, int secs, int *plock_depth)
67 if(*plock_depth == 0) {
68 if (!do_file_lock(fd, secs, type)) {
69 DEBUG(10,("pw_file_lock: locking file failed, error = %s.\n",
80 /***************************************************************
81 Unlock an fd. Abandon after waitsecs seconds.
82 ****************************************************************/
84 static BOOL pw_file_unlock(int fd, int *plock_depth)
89 ret = do_file_lock(fd, 5, F_UNLCK);
95 DEBUG(10,("pw_file_unlock: unlocking file failed, error = %s.\n",
101 /**************************************************************
102 Intialize a smb_passwd struct
103 *************************************************************/
105 static void pdb_init_smb(struct smb_passwd *user)
111 user->pass_last_set_time = (time_t)0;
114 /***************************************************************
115 Internal fn to enumerate the smbpasswd list. Returns a void pointer
116 to ensure no modification outside this module. Checks for atomic
117 rename of smbpasswd file on update or create once the lock has
118 been granted to prevent race conditions. JRA.
119 ****************************************************************/
121 static void *startsmbfilepwent(const char *pfile, enum pwf_access_type type, int *lock_depth)
124 const char *open_mode = NULL;
126 int lock_type = F_RDLCK;
129 DEBUG(0, ("startsmbfilepwent: No SMB password file set\n"));
144 * Ensure atomic file creation.
149 for(i = 0; i < 5; i++) {
150 if((fd = sys_open(pfile, O_CREAT|O_TRUNC|O_EXCL|O_RDWR, 0600))!=-1)
152 sys_usleep(200); /* Spin, spin... */
155 DEBUG(0,("startsmbfilepwent_internal: too many race conditions creating file %s\n", pfile));
165 for(race_loop = 0; race_loop < 5; race_loop++) {
166 DEBUG(10, ("startsmbfilepwent_internal: opening file %s\n", pfile));
168 if((fp = sys_fopen(pfile, open_mode)) == NULL) {
169 DEBUG(0, ("startsmbfilepwent_internal: unable to open file %s. Error was %s\n", pfile, strerror(errno) ));
173 if (!pw_file_lock(fileno(fp), lock_type, 5, lock_depth)) {
174 DEBUG(0, ("startsmbfilepwent_internal: unable to lock file %s. Error was %s\n", pfile, strerror(errno) ));
180 * Only check for replacement races on update or create.
181 * For read we don't mind if the data is one record out of date.
184 if(type == PWF_READ) {
187 SMB_STRUCT_STAT sbuf1, sbuf2;
190 * Avoid the potential race condition between the open and the lock
191 * by doing a stat on the filename and an fstat on the fd. If the
192 * two inodes differ then someone did a rename between the open and
193 * the lock. Back off and try the open again. Only do this 5 times to
194 * prevent infinate loops. JRA.
197 if (sys_stat(pfile,&sbuf1) != 0) {
198 DEBUG(0, ("startsmbfilepwent_internal: unable to stat file %s. Error was %s\n", pfile, strerror(errno)));
199 pw_file_unlock(fileno(fp), lock_depth);
204 if (sys_fstat(fileno(fp),&sbuf2) != 0) {
205 DEBUG(0, ("startsmbfilepwent_internal: unable to fstat file %s. Error was %s\n", pfile, strerror(errno)));
206 pw_file_unlock(fileno(fp), lock_depth);
211 if( sbuf1.st_ino == sbuf2.st_ino) {
217 * Race occurred - back off and try again...
220 pw_file_unlock(fileno(fp), lock_depth);
226 DEBUG(0, ("startsmbfilepwent_internal: too many race conditions opening file %s\n", pfile));
230 /* Set a buffer to do more efficient reads */
231 setvbuf(fp, (char *)NULL, _IOFBF, 1024);
233 /* Make sure it is only rw by the owner */
234 if(fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) {
235 DEBUG(0, ("startsmbfilepwent_internal: failed to set 0600 permissions on password file %s. \
236 Error was %s\n.", pfile, strerror(errno) ));
237 pw_file_unlock(fileno(fp), lock_depth);
242 /* We have a lock on the file. */
246 /***************************************************************
247 End enumeration of the smbpasswd list.
248 ****************************************************************/
249 static void endsmbfilepwent(void *vp, int *lock_depth)
251 FILE *fp = (FILE *)vp;
253 pw_file_unlock(fileno(fp), lock_depth);
255 DEBUG(7, ("endsmbfilepwent_internal: closed password file.\n"));
258 /*************************************************************************
259 Routine to return the next entry in the smbpasswd list.
260 *************************************************************************/
262 static struct smb_passwd *getsmbfilepwent(void *vp)
264 /* Static buffers we will return. */
265 static struct smb_passwd pw_buf;
266 static pstring user_name;
267 static unsigned char smbpwd[16];
268 static unsigned char smbntpwd[16];
269 FILE *fp = (FILE *)vp;
277 DEBUG(0,("getsmbfilepwent: Bad password file pointer.\n"));
281 pdb_init_smb(&pw_buf);
283 pw_buf.acct_ctrl = ACB_NORMAL;
286 * Scan the file, a line at a time and check if the name matches.
291 fgets(linebuf, 256, fp);
297 * Check if the string is terminated with a newline - if not
298 * then we must keep reading and discard until we get one.
300 if ((linebuf_len = strlen(linebuf)) == 0)
303 if (linebuf[linebuf_len - 1] != '\n') {
305 while (!ferror(fp) && !feof(fp)) {
311 linebuf[linebuf_len - 1] = '\0';
313 #ifdef DEBUG_PASSWORD
314 DEBUG(100, ("getsmbfilepwent: got line |%s|\n", linebuf));
316 if ((linebuf[0] == 0) && feof(fp)) {
317 DEBUG(4, ("getsmbfilepwent: end of file reached\n"));
321 * The line we have should be of the form :-
323 * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently
328 * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored....
330 * if Windows NT compatible passwords are also present.
331 * [Account type] is an ascii encoding of the type of account.
332 * LCT-(8 hex digits) is the time_t value of the last change time.
335 if (linebuf[0] == '#' || linebuf[0] == '\0') {
336 DEBUG(6, ("getsmbfilepwent: skipping comment or blank line\n"));
339 p = (unsigned char *) strchr_m(linebuf, ':');
341 DEBUG(0, ("getsmbfilepwent: malformed password entry (no :)\n"));
345 * As 256 is shorter than a pstring we don't need to check
346 * length here - if this ever changes....
348 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
349 user_name[PTR_DIFF(p, linebuf)] = '\0';
353 p++; /* Go past ':' */
356 DEBUG(0, ("getsmbfilepwent: uids in the smbpasswd file must not be negative.\n"));
361 DEBUG(0, ("getsmbfilepwent: malformed password entry (uid not number)\n"));
365 uidval = atoi((char *) p);
367 while (*p && isdigit(*p))
371 DEBUG(0, ("getsmbfilepwent: malformed password entry (no : after uid)\n"));
375 pw_buf.smb_name = user_name;
376 pw_buf.smb_userid = uidval;
379 * Now get the password value - this should be 32 hex digits
380 * which are the ascii representations of a 16 byte string.
381 * Get two at a time and put them into the password.
387 if (*p == '*' || *p == 'X') {
388 /* Password deliberately invalid - end here. */
389 DEBUG(10, ("getsmbfilepwent: entry invalidated for user %s\n", user_name));
390 pw_buf.smb_nt_passwd = NULL;
391 pw_buf.smb_passwd = NULL;
392 pw_buf.acct_ctrl |= ACB_DISABLED;
396 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
397 DEBUG(0, ("getsmbfilepwent: malformed password entry (passwd too short)\n"));
402 DEBUG(0, ("getsmbfilepwent: malformed password entry (no terminating :)\n"));
406 if (!strncasecmp((char *) p, "NO PASSWORD", 11)) {
407 pw_buf.smb_passwd = NULL;
408 pw_buf.acct_ctrl |= ACB_PWNOTREQ;
410 if (!pdb_gethexpwd((char *)p, smbpwd)) {
411 DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry (non hex chars)\n"));
414 pw_buf.smb_passwd = smbpwd;
418 * Now check if the NT compatible password is
421 pw_buf.smb_nt_passwd = NULL;
423 p += 33; /* Move to the first character of the line after
424 the lanman password. */
425 if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
426 if (*p != '*' && *p != 'X') {
427 if(pdb_gethexpwd((char *)p,smbntpwd))
428 pw_buf.smb_nt_passwd = smbntpwd;
430 p += 33; /* Move to the first character of the line after
434 DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n",
439 unsigned char *end_p = (unsigned char *)strchr_m((char *)p, ']');
440 pw_buf.acct_ctrl = pdb_decode_acct_ctrl((char*)p);
442 /* Must have some account type set. */
443 if(pw_buf.acct_ctrl == 0)
444 pw_buf.acct_ctrl = ACB_NORMAL;
446 /* Now try and get the last change time. */
451 if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) {
454 for(i = 0; i < 8; i++) {
455 if(p[i] == '\0' || !isxdigit(p[i]))
460 * p points at 8 characters of hex digits -
461 * read into a time_t as the seconds since
462 * 1970 that the password was last changed.
464 pw_buf.pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
469 /* 'Old' style file. Fake up based on user name. */
471 * Currently trust accounts are kept in the same
472 * password file as 'normal accounts'. If this changes
473 * we will have to fix this code. JRA.
475 if(pw_buf.smb_name[strlen(pw_buf.smb_name) - 1] == '$') {
476 pw_buf.acct_ctrl &= ~ACB_NORMAL;
477 pw_buf.acct_ctrl |= ACB_WSTRUST;
484 DEBUG(5,("getsmbfilepwent: end of file reached.\n"));
488 /************************************************************************
489 Create a new smbpasswd entry - malloced space returned.
490 *************************************************************************/
492 static char *format_new_smbpasswd_entry(struct smb_passwd *newpwd)
494 int new_entry_length;
499 new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2;
501 if((new_entry = (char *)malloc( new_entry_length )) == NULL) {
502 DEBUG(0, ("format_new_smbpasswd_entry: Malloc failed adding entry for user %s.\n", newpwd->smb_name ));
506 slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid);
507 p = &new_entry[strlen(new_entry)];
509 if(newpwd->smb_passwd != NULL) {
510 for( i = 0; i < 16; i++) {
511 slprintf((char *)&p[i*2], new_entry_length - (p - new_entry) - 1, "%02X", newpwd->smb_passwd[i]);
515 if(newpwd->acct_ctrl & ACB_PWNOTREQ)
516 safe_strcpy((char *)p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
518 safe_strcpy((char *)p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
525 if(newpwd->smb_nt_passwd != NULL) {
526 for( i = 0; i < 16; i++) {
527 slprintf((char *)&p[i*2], new_entry_length - 1 - (p - new_entry), "%02X", newpwd->smb_nt_passwd[i]);
530 if(newpwd->acct_ctrl & ACB_PWNOTREQ)
531 safe_strcpy((char *)p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
533 safe_strcpy((char *)p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
540 /* Add the account encoding and the last change time. */
541 slprintf((char *)p, new_entry_length - 1 - (p - new_entry), "%s:LCT-%08X:\n",
542 pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN),
543 (uint32)newpwd->pass_last_set_time);
548 /************************************************************************
549 Routine to add an entry to the smbpasswd file.
550 *************************************************************************/
552 static BOOL add_smbfilepwd_entry(struct smb_passwd *newpwd)
554 char *pfile = lp_smb_passwd_file();
555 struct smb_passwd *pwd = NULL;
559 size_t new_entry_length;
563 /* Open the smbpassword file - for update. */
564 fp = startsmbfilepwent(pfile, PWF_UPDATE, &pw_file_lock_depth);
566 if (fp == NULL && errno == ENOENT) {
567 /* Try again - create. */
568 fp = startsmbfilepwent(pfile, PWF_CREATE, &pw_file_lock_depth);
572 DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n"));
577 * Scan the file, a line at a time and check if the name matches.
580 while ((pwd = getsmbfilepwent(fp)) != NULL)
582 if (strequal(newpwd->smb_name, pwd->smb_name))
584 DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name));
585 endsmbfilepwent(fp, &pw_file_lock_depth);
590 /* Ok - entry doesn't exist. We can add it */
592 /* Create a new smb passwd entry and set it to the given password. */
594 * The add user write needs to be atomic - so get the fd from
595 * the fp and do a raw write() call.
599 if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1)
601 DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \
602 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
603 endsmbfilepwent(fp, &pw_file_lock_depth);
607 if((new_entry = format_new_smbpasswd_entry(newpwd)) == NULL)
609 DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
610 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
611 endsmbfilepwent(fp, &pw_file_lock_depth);
615 new_entry_length = strlen(new_entry);
617 #ifdef DEBUG_PASSWORD
618 DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d made line |%s|",
619 fd, new_entry_length, new_entry));
622 if ((wr_len = write(fd, new_entry, new_entry_length)) != new_entry_length)
624 DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \
625 Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
627 /* Remove the entry we just wrote. */
628 if(sys_ftruncate(fd, offpos) == -1)
630 DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \
631 Error was %s. Password file may be corrupt ! Please examine by hand !\n",
632 newpwd->smb_name, strerror(errno)));
635 endsmbfilepwent(fp, &pw_file_lock_depth);
641 endsmbfilepwent(fp, &pw_file_lock_depth);
645 /************************************************************************
646 Routine to search the smbpasswd file for an entry matching the username.
647 and then modify its password entry. We can't use the startsmbpwent()/
648 getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
649 in the actual file to decide how much room we have to write data.
650 override = False, normal
651 override = True, override XXXXXXXX'd out password or NO PASS
652 ************************************************************************/
654 static BOOL mod_smbfilepwd_entry(struct smb_passwd* pwd, BOOL override)
656 /* Static buffers we will return. */
657 static pstring user_name;
664 unsigned char *p = NULL;
665 size_t linebuf_len = 0;
668 char *pfile = lp_smb_passwd_file();
669 BOOL found_entry = False;
670 BOOL got_pass_last_set_time = False;
672 SMB_OFF_T pwd_seekpos = 0;
679 DEBUG(0, ("No SMB password file set\n"));
682 DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
684 fp = sys_fopen(pfile, "r+");
687 DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
690 /* Set a buffer to do more efficient reads */
691 setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
695 if (!pw_file_lock(lockfd, F_WRLCK, 5, &pw_file_lock_depth)) {
696 DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile));
701 /* Make sure it is only rw by the owner */
704 /* We have a write lock on the file. */
706 * Scan the file, a line at a time and check if the name matches.
709 pwd_seekpos = sys_ftell(fp);
713 fgets(linebuf, sizeof(linebuf), fp);
715 pw_file_unlock(lockfd, &pw_file_lock_depth);
721 * Check if the string is terminated with a newline - if not
722 * then we must keep reading and discard until we get one.
724 linebuf_len = strlen(linebuf);
725 if (linebuf[linebuf_len - 1] != '\n') {
727 while (!ferror(fp) && !feof(fp)) {
734 linebuf[linebuf_len - 1] = '\0';
737 #ifdef DEBUG_PASSWORD
738 DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
741 if ((linebuf[0] == 0) && feof(fp)) {
742 DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
747 * The line we have should be of the form :-
749 * username:uid:[32hex bytes]:....other flags presently
754 * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
756 * if Windows NT compatible passwords are also present.
759 if (linebuf[0] == '#' || linebuf[0] == '\0') {
760 DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
764 p = (unsigned char *) strchr_m(linebuf, ':');
767 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
772 * As 256 is shorter than a pstring we don't need to check
773 * length here - if this ever changes....
775 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
776 user_name[PTR_DIFF(p, linebuf)] = '\0';
777 if (strequal(user_name, pwd->smb_name)) {
784 pw_file_unlock(lockfd, &pw_file_lock_depth);
789 DEBUG(6, ("mod_smbfilepwd_entry: entry exists\n"));
791 /* User name matches - get uid and password */
792 p++; /* Go past ':' */
795 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (uid not number)\n"));
796 pw_file_unlock(lockfd, &pw_file_lock_depth);
801 while (*p && isdigit(*p))
804 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no : after uid)\n"));
805 pw_file_unlock(lockfd, &pw_file_lock_depth);
811 * Now get the password value - this should be 32 hex digits
812 * which are the ascii representations of a 16 byte string.
813 * Get two at a time and put them into the password.
817 /* Record exact password position */
818 pwd_seekpos += PTR_DIFF(p, linebuf);
820 if (!override && (*p == '*' || *p == 'X')) {
821 /* Password deliberately invalid - end here. */
822 DEBUG(10, ("mod_smbfilepwd_entry: entry invalidated for user %s\n", user_name));
823 pw_file_unlock(lockfd, &pw_file_lock_depth);
828 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
829 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
830 pw_file_unlock(lockfd,&pw_file_lock_depth);
836 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
837 pw_file_unlock(lockfd,&pw_file_lock_depth);
842 if (!override && (*p == '*' || *p == 'X')) {
843 pw_file_unlock(lockfd,&pw_file_lock_depth);
848 /* Now check if the NT compatible password is
850 p += 33; /* Move to the first character of the line after
851 the lanman password. */
852 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
853 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
854 pw_file_unlock(lockfd,&pw_file_lock_depth);
860 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
861 pw_file_unlock(lockfd,&pw_file_lock_depth);
867 * Now check if the account info and the password last
868 * change time is available.
870 p += 33; /* Move to the first character of the line after
876 encode_bits[i++] = *p++;
877 while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']'))
878 encode_bits[i++] = *p++;
880 encode_bits[i++] = ']';
881 encode_bits[i++] = '\0';
883 if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
885 * We are using a new format, space padded
886 * acct ctrl field. Encode the given acct ctrl
889 fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
891 DEBUG(0,("mod_smbfilepwd_entry: Using old smbpasswd format. This is no longer supported.!\n"));
892 DEBUG(0,("mod_smbfilepwd_entry: No changes made, failing.!\n"));
896 /* Go past the ']' */
897 if(linebuf_len > PTR_DIFF(p, linebuf))
900 if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
903 /* We should be pointing at the LCT entry. */
904 if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
907 for(i = 0; i < 8; i++) {
908 if(p[i] == '\0' || !isxdigit(p[i]))
913 * p points at 8 characters of hex digits -
914 * read into a time_t as the seconds since
915 * 1970 that the password was last changed.
917 got_pass_last_set_time = True;
919 } /* *p && StrnCaseCmp() */
923 /* Entry is correctly formed. */
925 /* Create the 32 byte representation of the new p16 */
926 if(pwd->smb_passwd != NULL) {
927 for (i = 0; i < 16; i++) {
928 slprintf(&ascii_p16[i*2], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_passwd[i]);
931 if(pwd->acct_ctrl & ACB_PWNOTREQ)
932 fstrcpy(ascii_p16, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
934 fstrcpy(ascii_p16, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
937 /* Add on the NT md4 hash */
940 if (pwd->smb_nt_passwd != NULL) {
941 for (i = 0; i < 16; i++) {
942 slprintf(&ascii_p16[(i*2)+33], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_nt_passwd[i]);
945 if(pwd->acct_ctrl & ACB_PWNOTREQ)
946 fstrcpy(&ascii_p16[33], "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
948 fstrcpy(&ascii_p16[33], "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
951 ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
953 /* Add on the account info bits and the time of last
956 if(got_pass_last_set_time) {
957 slprintf(&ascii_p16[strlen(ascii_p16)],
958 sizeof(ascii_p16)-(strlen(ascii_p16)+1),
960 encode_bits, (uint32)pwd->pass_last_set_time );
961 wr_len = strlen(ascii_p16);
964 #ifdef DEBUG_PASSWORD
965 DEBUG(100,("mod_smbfilepwd_entry: "));
966 dump_data(100, ascii_p16, wr_len);
969 if(wr_len > sizeof(linebuf)) {
970 DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
971 pw_file_unlock(lockfd,&pw_file_lock_depth);
977 * Do an atomic write into the file at the position defined by
981 /* The mod user write needs to be atomic - so get the fd from
982 the fp and do a raw write() call.
987 if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
988 DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
989 pw_file_unlock(lockfd,&pw_file_lock_depth);
994 /* Sanity check - ensure the areas we are writing are framed by ':' */
995 if (read(fd, linebuf, wr_len+1) != wr_len+1) {
996 DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
997 pw_file_unlock(lockfd,&pw_file_lock_depth);
1002 if ((linebuf[0] != ':') || (linebuf[wr_len] != ':')) {
1003 DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
1004 pw_file_unlock(lockfd,&pw_file_lock_depth);
1009 if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
1010 DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1011 pw_file_unlock(lockfd,&pw_file_lock_depth);
1016 if (write(fd, ascii_p16, wr_len) != wr_len) {
1017 DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
1018 pw_file_unlock(lockfd,&pw_file_lock_depth);
1023 pw_file_unlock(lockfd,&pw_file_lock_depth);
1028 /************************************************************************
1029 Routine to delete an entry in the smbpasswd file by name.
1030 *************************************************************************/
1032 static BOOL del_smbfilepwd_entry(const char *name)
1034 char *pfile = lp_smb_passwd_file();
1036 struct smb_passwd *pwd = NULL;
1038 FILE *fp_write = NULL;
1039 int pfile2_lockdepth = 0;
1041 slprintf(pfile2, sizeof(pfile2)-1, "%s.%u", pfile, (unsigned)sys_getpid() );
1044 * Open the smbpassword file - for update. It needs to be update
1045 * as we need any other processes to wait until we have replaced
1049 if((fp = startsmbfilepwent(pfile, PWF_UPDATE, &pw_file_lock_depth)) == NULL) {
1050 DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1055 * Create the replacement password file.
1057 if((fp_write = startsmbfilepwent(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) {
1058 DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1059 endsmbfilepwent(fp, &pw_file_lock_depth);
1064 * Scan the file, a line at a time and check if the name matches.
1067 while ((pwd = getsmbfilepwent(fp)) != NULL) {
1069 size_t new_entry_length;
1071 if (strequal(name, pwd->smb_name)) {
1072 DEBUG(10, ("add_smbfilepwd_entry: found entry with name %s - deleting it.\n", name));
1077 * We need to copy the entry out into the second file.
1080 if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL)
1082 DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \
1083 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1085 endsmbfilepwent(fp, &pw_file_lock_depth);
1086 endsmbfilepwent(fp_write, &pfile2_lockdepth);
1090 new_entry_length = strlen(new_entry);
1092 if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length)
1094 DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \
1095 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1097 endsmbfilepwent(fp, &pw_file_lock_depth);
1098 endsmbfilepwent(fp_write, &pfile2_lockdepth);
1107 * Ensure pfile2 is flushed before rename.
1110 if(fflush(fp_write) != 0)
1112 DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno)));
1113 endsmbfilepwent(fp, &pw_file_lock_depth);
1114 endsmbfilepwent(fp_write,&pfile2_lockdepth);
1119 * Do an atomic rename - then release the locks.
1122 if(rename(pfile2,pfile) != 0) {
1126 endsmbfilepwent(fp, &pw_file_lock_depth);
1127 endsmbfilepwent(fp_write,&pfile2_lockdepth);
1131 /*********************************************************************
1132 Create a smb_passwd struct from a SAM_ACCOUNT.
1133 We will not allocate any new memory. The smb_passwd struct
1134 should only stay around as long as the SAM_ACCOUNT does.
1135 ********************************************************************/
1136 static BOOL build_smb_pass (struct smb_passwd *smb_pw, const SAM_ACCOUNT *sampass)
1138 if (sampass == NULL)
1141 ZERO_STRUCTP(smb_pw);
1143 smb_pw->smb_userid=pdb_get_uid(sampass);
1144 smb_pw->smb_name=pdb_get_username(sampass);
1146 smb_pw->smb_passwd=pdb_get_lanman_passwd(sampass);
1147 smb_pw->smb_nt_passwd=pdb_get_nt_passwd(sampass);
1149 smb_pw->acct_ctrl=pdb_get_acct_ctrl(sampass);
1150 smb_pw->pass_last_set_time=pdb_get_pass_last_set_time(sampass);
1152 if (smb_pw->smb_userid != pdb_user_rid_to_uid(pdb_get_user_rid(sampass))) {
1153 DEBUG(0,("build_sam_pass: Failing attempt to store user with non-uid based user RID. \n"));
1157 if (pdb_get_gid(sampass) != pdb_group_rid_to_gid(pdb_get_group_rid(sampass))) {
1158 DEBUG(0,("build_sam_pass: Failing attempt to store user with non-gid based primary group RID. \n"));
1165 /*********************************************************************
1166 Create a SAM_ACCOUNT from a smb_passwd struct
1167 ********************************************************************/
1168 static BOOL build_sam_account(SAM_ACCOUNT *sam_pass, const struct smb_passwd *pw_buf)
1170 struct passwd *pwfile;
1172 if (sam_pass==NULL) {
1173 DEBUG(5,("build_sam_account: SAM_ACCOUNT is NULL\n"));
1177 /* Verify in system password file...
1178 FIXME!!! This is where we should look up an internal
1179 mapping of allocated uid for machine accounts as well
1181 pwfile = sys_getpwnam(pw_buf->smb_name);
1182 if (pwfile == NULL) {
1183 DEBUG(0,("build_sam_account: smbpasswd database is corrupt! username %s not in unix passwd database!\n", pw_buf->smb_name));
1187 /* FIXME!! This doesn't belong here. Should be set in net_sam_logon()
1189 pstrcpy(samlogon_user, pw_buf->smb_name);
1191 sam_logon_in_ssb = True;
1193 pdb_set_uid (sam_pass, pwfile->pw_uid);
1194 pdb_set_gid (sam_pass, pwfile->pw_gid);
1195 pdb_set_fullname(sam_pass, pwfile->pw_gecos);
1197 pdb_set_user_rid(sam_pass, pdb_uid_to_user_rid (pwfile->pw_uid));
1199 /* should check the group mapping here instead of static mappig. JFM */
1200 pdb_set_group_rid(sam_pass, pdb_gid_to_group_rid(pwfile->pw_gid));
1202 pdb_set_username (sam_pass, pw_buf->smb_name);
1203 pdb_set_nt_passwd (sam_pass, pw_buf->smb_nt_passwd);
1204 pdb_set_lanman_passwd (sam_pass, pw_buf->smb_passwd);
1205 pdb_set_acct_ctrl (sam_pass, pw_buf->acct_ctrl);
1206 pdb_set_pass_last_set_time (sam_pass, pw_buf->pass_last_set_time);
1207 pdb_set_pass_can_change_time (sam_pass, pw_buf->pass_last_set_time);
1208 pdb_set_domain (sam_pass, lp_workgroup());
1210 pdb_set_dir_drive (sam_pass, lp_logon_drive());
1212 /* the smbpasswd format doesn't have a must change time field, so
1213 we can't get this right. The best we can do is to set this to
1214 some time in the future. 21 days seems as reasonable as any other value :)
1216 pdb_set_pass_must_change_time (sam_pass, pw_buf->pass_last_set_time + MAX_PASSWORD_AGE);
1218 /* check if this is a user account or a machine account */
1219 if (samlogon_user[strlen(samlogon_user)-1] != '$')
1223 pstrcpy(str, lp_logon_path());
1224 standard_sub_advanced(-1, pwfile->pw_name, "", pwfile->pw_gid, str);
1225 pdb_set_profile_path(sam_pass, str);
1227 pstrcpy(str, lp_logon_home());
1228 standard_sub_advanced(-1, pwfile->pw_name, "", pwfile->pw_gid, str);
1229 pdb_set_homedir(sam_pass, str);
1231 pstrcpy(str, lp_logon_drive());
1232 standard_sub_advanced(-1, pwfile->pw_name, "", pwfile->pw_gid, str);
1233 pdb_set_dir_drive(sam_pass, str);
1235 pstrcpy(str, lp_logon_script());
1236 standard_sub_advanced(-1, pwfile->pw_name, "", pwfile->pw_gid, str);
1237 pdb_set_logon_script(sam_pass, str);
1240 /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. */
1241 pdb_set_group_rid (sam_pass, DOMAIN_GROUP_RID_USERS);
1244 sam_logon_in_ssb = False;
1248 /*****************************************************************
1249 Functions to be implemented by the new passdb API
1250 ****************************************************************/
1251 BOOL pdb_setsampwent (BOOL update)
1253 global_vp = startsmbfilepwent(lp_smb_passwd_file(),
1254 update ? PWF_UPDATE : PWF_READ,
1255 &pw_file_lock_depth);
1257 /* did we fail? Should we try to create it? */
1258 if (!global_vp && update && errno == ENOENT)
1261 /* slprintf(msg_str,msg_str_len-1,
1262 "smbpasswd file did not exist - attempting to create it.\n"); */
1263 DEBUG(0,("smbpasswd file did not exist - attempting to create it.\n"));
1264 fp = sys_fopen(lp_smb_passwd_file(), "w");
1267 fprintf(fp, "# Samba SMB password file\n");
1271 global_vp = startsmbfilepwent(lp_smb_passwd_file(),
1272 update ? PWF_UPDATE : PWF_READ,
1273 &pw_file_lock_depth);
1276 return (global_vp != NULL);
1279 void pdb_endsampwent (void)
1281 endsmbfilepwent(global_vp, &pw_file_lock_depth);
1284 /*****************************************************************
1285 ****************************************************************/
1286 BOOL pdb_getsampwent(SAM_ACCOUNT *user)
1288 struct smb_passwd *pw_buf=NULL;
1290 DEBUG(5,("pdb_getsampwent\n"));
1293 DEBUG(5,("pdb_getsampwent: user is NULL\n"));
1295 smb_panic("NULL pointer passed to pdb_getsampwent\n");
1302 /* do we have an entry? */
1303 pw_buf = getsmbfilepwent(global_vp);
1307 /* build the SAM_ACCOUNT entry from the smb_passwd struct.
1308 We loop in case the user in the pdb does not exist in
1309 the local system password file */
1310 if (build_sam_account(user, pw_buf))
1314 DEBUG(5,("pdb_getsampwent:done\n"));
1321 /****************************************************************
1322 Search smbpasswd file by iterating over the entries. Do not
1323 call getpwnam() for unix account information until we have found
1325 ***************************************************************/
1326 BOOL pdb_getsampwnam(SAM_ACCOUNT *sam_acct, char *username)
1328 struct smb_passwd *smb_pw;
1330 char *domain = NULL;
1334 DEBUG(10, ("pdb_getsampwnam: search by name: %s\n", username));
1337 /* break the username from the domain if we have
1338 been given a string in the form 'DOMAIN\user' */
1339 fstrcpy (name, username);
1340 if ((user=strchr_m(name, '\\')) != NULL) {
1346 /* if a domain was specified and it wasn't ours
1347 then there is no chance of matching */
1348 if ( domain && !StrCaseCmp(domain, lp_workgroup()) )
1351 /* startsmbfilepwent() is used here as we don't want to lookup
1352 the UNIX account in the local system password file until
1354 fp = startsmbfilepwent(lp_smb_passwd_file(), PWF_READ, &pw_file_lock_depth);
1357 DEBUG(0, ("unable to open passdb database.\n"));
1361 /* if we have a domain name, then we should map it to a UNIX
1366 while ( ((smb_pw=getsmbfilepwent(fp)) != NULL)&& (!strequal(smb_pw->smb_name, username)) )
1367 /* do nothing....another loop */ ;
1369 endsmbfilepwent(fp, &pw_file_lock_depth);
1372 /* did we locate the username in smbpasswd */
1376 DEBUG(10, ("pdb_getsampwnam: found by name: %s\n", smb_pw->smb_name));
1379 DEBUG(10,("pdb_getsampwnam:SAM_ACCOUNT is NULL\n"));
1381 smb_panic("NULL pointer passed to pdb_getsampwnam\n");
1386 /* now build the SAM_ACCOUNT */
1387 if (!build_sam_account(sam_acct, smb_pw))
1395 BOOL pdb_getsampwuid (SAM_ACCOUNT *sam_acct, uid_t uid)
1397 struct smb_passwd *smb_pw;
1400 DEBUG(10, ("pdb_getsampwuid: search by uid: %d\n", uid));
1402 /* Open the sam password file - not for update. */
1403 fp = startsmbfilepwent(lp_smb_passwd_file(), PWF_READ, &pw_file_lock_depth);
1406 DEBUG(0, ("unable to open passdb database.\n"));
1410 while ( ((smb_pw=getsmbfilepwent(fp)) != NULL) && (smb_pw->smb_userid != uid) )
1413 endsmbfilepwent(fp, &pw_file_lock_depth);
1415 /* did we locate the username in smbpasswd */
1419 DEBUG(10, ("pdb_getsampwuid: found by name: %s\n", smb_pw->smb_name));
1422 DEBUG(10,("pdb_getsampwuid:SAM_ACCOUNT is NULL\n"));
1424 smb_panic("NULL pointer passed to pdb_getsampwuid\n");
1429 /* now build the SAM_ACCOUNT */
1430 if (!build_sam_account(sam_acct, smb_pw))
1437 BOOL pdb_getsampwrid(SAM_ACCOUNT *sam_acct,uint32 rid)
1439 struct smb_passwd *smb_pw;
1442 DEBUG(10, ("pdb_getsampwrid: search by rid: %d\n", rid));
1444 /* Open the sam password file - not for update. */
1445 fp = startsmbfilepwent(lp_smb_passwd_file(), PWF_READ, &pw_file_lock_depth);
1448 DEBUG(0, ("unable to open passdb database.\n"));
1452 while ( ((smb_pw=getsmbfilepwent(fp)) != NULL) && (pdb_uid_to_user_rid(smb_pw->smb_userid) != rid) )
1455 endsmbfilepwent(fp, &pw_file_lock_depth);
1458 /* did we locate the username in smbpasswd */
1462 DEBUG(10, ("pdb_getsampwrid: found by name: %s\n", smb_pw->smb_name));
1465 DEBUG(10,("pdb_getsampwrid:SAM_ACCOUNT is NULL\n"));
1467 smb_panic("NULL pointer passed to pdb_getsampwrid\n");
1472 /* now build the SAM_ACCOUNT */
1473 if (!build_sam_account (sam_acct, smb_pw))
1480 BOOL pdb_add_sam_account(const SAM_ACCOUNT *sampass)
1482 struct smb_passwd smb_pw;
1484 /* convert the SAM_ACCOUNT */
1485 if (!build_smb_pass(&smb_pw, sampass)) {
1490 if(!add_smbfilepwd_entry(&smb_pw)) {
1497 BOOL pdb_update_sam_account(const SAM_ACCOUNT *sampass, BOOL override)
1499 struct smb_passwd smb_pw;
1501 /* convert the SAM_ACCOUNT */
1502 build_smb_pass(&smb_pw, sampass);
1504 /* update the entry */
1505 if(!mod_smbfilepwd_entry(&smb_pw, override))
1511 BOOL pdb_delete_sam_account (char* username)
1513 return del_smbfilepwd_entry(username);
1517 /* Do *NOT* make this function static. It breaks the compile on gcc. JRA */
1518 void smbpass_dummy_function(void) { } /* stop some compilers complaining */
1519 #endif /* WTH_SMBPASSWD_SAM*/