2 * Unix SMB/Netbios implementation. Version 1.9. SMB parameters and setup
3 * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995.
5 * This program is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 675
17 * Mass Ave, Cambridge, MA 02139, USA.
22 extern int DEBUGLEVEL;
25 static char s_readbuf[16 * 1024];
27 /***************************************************************
28 Signal function to tell us we timed out.
29 ****************************************************************/
31 static void gotalarm_sig()
36 /***************************************************************
37 Lock or unlock a fd for a known lock type. Abandon after waitsecs
39 ****************************************************************/
41 static int do_pw_lock(int fd, int waitsecs, int type)
47 signal(SIGALRM, SIGNAL_CAST gotalarm_sig);
50 lock.l_whence = SEEK_SET;
56 ret = fcntl(fd, F_SETLKW, &lock);
58 signal(SIGALRM, SIGNAL_CAST SIG_DFL);
61 DEBUG(0, ("do_pw_lock: failed to %s SMB passwd file.\n",
62 type == F_UNLCK ? "unlock" : "lock"));
68 /***************************************************************
69 Lock an fd. Abandon after waitsecs seconds.
70 ****************************************************************/
72 int pw_file_lock(int fd, int type, int secs)
76 if (do_pw_lock(fd, secs, type)) {
82 /***************************************************************
83 Unlock an fd. Abandon after waitsecs seconds.
84 ****************************************************************/
86 int pw_file_unlock(int fd)
88 return do_pw_lock(fd, 5, F_UNLCK);
91 /***************************************************************
92 Open the smbpasswd file - get ready to enumerate it.
93 ****************************************************************/
95 FILE *startsmbpwent(BOOL update)
98 char *pfile = lp_smb_passwd_file();
101 DEBUG(0, ("startsmbpwent: No SMB password file set\n"));
104 DEBUG(10, ("startsmbpwent: opening file %s\n", pfile));
106 fp = fopen(pfile, update ? "r+b" : "rb");
109 DEBUG(0, ("startsmbpwent: unable to open file %s\n", pfile));
113 /* Set a 16k buffer to do more efficient reads */
114 setvbuf(fp, s_readbuf, _IOFBF, sizeof(s_readbuf));
116 if ((pw_file_lock(fileno(fp), F_RDLCK | (update ? F_WRLCK : 0), 5)) < 0) {
117 DEBUG(0, ("startsmbpwent: unable to lock file %s\n", pfile));
122 /* Make sure it is only rw by the owner */
125 /* We have a lock on the file. */
129 /***************************************************************
130 Close the smbpasswd file - end enumeration.
131 ****************************************************************/
133 void endsmbpwent(FILE *fp)
135 pw_file_unlock(fileno(fp));
137 DEBUG(7, ("endsmbpwent: closed password file.\n"));
140 /*************************************************************
141 Routine to get the next 32 hex characters and turn them
142 into a 16 byte array.
143 **************************************************************/
145 static int gethexpwd(char *p, char *pwd)
148 unsigned char lonybble, hinybble;
149 char *hexchars = "0123456789ABCDEF";
152 for (i = 0; i < 32; i += 2) {
153 hinybble = toupper(p[i]);
154 lonybble = toupper(p[i + 1]);
156 p1 = strchr(hexchars, hinybble);
157 p2 = strchr(hexchars, lonybble);
160 hinybble = PTR_DIFF(p1, hexchars);
161 lonybble = PTR_DIFF(p2, hexchars);
163 pwd[i / 2] = (hinybble << 4) | lonybble;
168 /*************************************************************************
169 Routine to return the next entry in the smbpasswd file.
170 *************************************************************************/
172 struct smb_passwd *getsmbpwent(FILE *fp)
174 /* Static buffers we will return. */
175 static struct smb_passwd pw_buf;
176 static pstring user_name;
177 static unsigned char smbpwd[16];
178 static unsigned char smbntpwd[16];
186 DEBUG(0,("getsmbpwent: Bad password file pointer.\n"));
191 * Scan the file, a line at a time and check if the name matches.
196 fgets(linebuf, 256, fp);
202 * Check if the string is terminated with a newline - if not
203 * then we must keep reading and discard until we get one.
205 linebuf_len = strlen(linebuf);
206 if (linebuf[linebuf_len - 1] != '\n') {
208 while (!ferror(fp) && !feof(fp)) {
214 linebuf[linebuf_len - 1] = '\0';
216 #ifdef DEBUG_PASSWORD
217 DEBUG(100, ("getsmbpwent: got line |%s|\n", linebuf));
219 if ((linebuf[0] == 0) && feof(fp)) {
220 DEBUG(4, ("getsmbpwent: end of file reached\n"));
224 * The line we have should be of the form :-
226 * username:uid:[32hex bytes]:....other flags presently
231 * username:uid:[32hex bytes]:[32hex bytes]:....ignored....
233 * if Windows NT compatible passwords are also present.
236 if (linebuf[0] == '#' || linebuf[0] == '\0') {
237 DEBUG(6, ("getsmbpwent: skipping comment or blank line\n"));
240 p = (unsigned char *) strchr(linebuf, ':');
242 DEBUG(0, ("getsmbpwent: malformed password entry (no :)\n"));
246 * As 256 is shorter than a pstring we don't need to check
247 * length here - if this ever changes....
249 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
250 user_name[PTR_DIFF(p, linebuf)] = '\0';
254 p++; /* Go past ':' */
256 DEBUG(0, ("getsmbpwent: malformed password entry (uid not number)\n"));
260 uidval = atoi((char *) p);
262 while (*p && isdigit(*p))
266 DEBUG(0, ("getsmbpwent: malformed password entry (no : after uid)\n"));
270 pw_buf.smb_name = user_name;
271 pw_buf.smb_userid = uidval;
274 * Now get the password value - this should be 32 hex digits
275 * which are the ascii representations of a 16 byte string.
276 * Get two at a time and put them into the password.
282 if (*p == '*' || *p == 'X') {
283 /* Password deliberately invalid - end here. */
284 DEBUG(10, ("getsmbpwent: entry invalidated for user %s\n", user_name));
285 pw_buf.smb_nt_passwd = NULL;
286 pw_buf.smb_passwd = NULL;
287 pw_buf.acct_ctrl |= ACB_DISABLED;
291 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
292 DEBUG(0, ("getsmbpwent: malformed password entry (passwd too short)\n"));
297 DEBUG(0, ("getsmbpwent: malformed password entry (no terminating :)\n"));
301 if (!strncasecmp((char *) p, "NO PASSWORD", 11)) {
302 pw_buf.smb_passwd = NULL;
303 pw_buf.acct_ctrl |= ACB_PWNOTREQ;
305 if (!gethexpwd((char *)p, (char *)smbpwd)) {
306 DEBUG(0, ("getsmbpwent: Malformed Lanman password entry (non hex chars)\n"));
309 pw_buf.smb_passwd = smbpwd;
313 * Now check if the NT compatible password is
316 pw_buf.smb_nt_passwd = NULL;
318 p += 33; /* Move to the first character of the line after
319 the lanman password. */
320 if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
321 if (*p != '*' && *p != 'X') {
322 if(gethexpwd((char *)p,(char *)smbntpwd))
323 pw_buf.smb_nt_passwd = smbntpwd;
325 p += 33; /* Move to the first character of the line after
329 DEBUG(5, ("getsmbpwent: returning passwd entry for user %s, uid %d\n",
333 * Check if the account type bits have been encoded after the
334 * NT password (in the form [NDHTUWSLXI]).
338 BOOL finished = False;
340 pw_buf.acct_ctrl = 0;
342 for(p++;*p && !finished; p++) {
346 pw_buf.acct_ctrl |= ACB_PWNOTREQ;
350 pw_buf.acct_ctrl |= ACB_DISABLED;
353 /* 'H'omedir required. */
354 pw_buf.acct_ctrl |= ACB_HOMDIRREQ;
357 /* 'T'emp account. */
358 pw_buf.acct_ctrl |= ACB_TEMPDUP;
361 /* 'U'ser account (normal). */
362 pw_buf.acct_ctrl |= ACB_NORMAL;
365 /* 'M'NS logon user account. What is this ? */
366 pw_buf.acct_ctrl |= ACB_MNS;
369 /* 'W'orkstation account. */
370 pw_buf.acct_ctrl |= ACB_WSTRUST;
373 /* 'S'erver account. */
374 pw_buf.acct_ctrl |= ACB_SVRTRUST;
377 /* 'L'ocked account. */
378 pw_buf.acct_ctrl |= ACB_AUTOLOCK;
382 pw_buf.acct_ctrl |= ACB_PWNOEXP;
385 /* 'I'nterdomain trust account. */
386 pw_buf.acct_ctrl |= ACB_DOMTRUST;
398 /* Must have some account type set. */
399 if(pw_buf.acct_ctrl == 0)
400 pw_buf.acct_ctrl = ACB_NORMAL;
403 /* 'Old' style file. Fake up based on user name. */
405 * Currently machine accounts are kept in the same
406 * password file as 'normal accounts'. If this changes
407 * we will have to fix this code. JRA.
409 if(pw_buf.smb_name[strlen(pw_buf.smb_name) - 1] == '$')
410 pw_buf.acct_ctrl = ACB_WSTRUST;
412 pw_buf.acct_ctrl = ACB_NORMAL;
418 DEBUG(5,("getsmbpwent: end of file reached.\n"));
422 /*************************************************************************
423 Routine to search the smbpasswd file for an entry matching the username
424 or user id. if the name is NULL, then the smb_uid is used instead.
425 *************************************************************************/
427 struct smb_passwd *get_smbpwd_entry(char *name, int smb_userid)
429 struct smb_passwd *pwd = NULL;
433 DEBUG(10, ("get_smbpwd_entry: search by name: %s\n", name));
435 DEBUG(10, ("get_smbpwd_entry: search by smb_userid: %x\n", smb_userid));
438 /* Open the smbpassword file - not for update. */
439 fp = startsmbpwent(False);
442 DEBUG(0, ("get_smbpwd_entry: unable to open password file.\n"));
447 * Scan the file, a line at a time and check if the name
451 while ((pwd = getsmbpwent(fp)) != NULL) {
453 /* Search is by user name */
454 if (!strequal(pwd->smb_name, name))
456 DEBUG(10, ("get_smbpwd_entry: found by name: %s\n", name));
459 /* Search is by user id */
460 if (pwd->smb_userid != smb_userid)
462 DEBUG(10, ("get_smbpwd_entry: found by smb_userid: %x\n", smb_userid));
471 /************************************************************************
472 Routine to add an entry to the smbpasswd file.
473 *************************************************************************/
475 BOOL add_smbpwd_entry(struct smb_passwd *newpwd)
477 char *pfile = lp_smb_passwd_file();
478 struct smb_passwd *pwd = NULL;
485 int new_entry_length;
490 /* Open the smbpassword file - for update. */
491 fp = startsmbpwent(True);
494 DEBUG(0, ("add_smbpwd_entry: unable to open file.\n"));
499 * Scan the file, a line at a time and check if the name matches.
502 while ((pwd = getsmbpwent(fp)) != NULL) {
503 if (strequal(newpwd->smb_name, pwd->smb_name)) {
504 DEBUG(0, ("add_smbpwd_entry: entry with name %s already exists\n", pwd->smb_name));
510 /* Ok - entry doesn't exist. We can add it */
512 /* Create a new smb passwd entry and set it to the given password. */
514 * The add user write needs to be atomic - so get the fd from
515 * the fp and do a raw write() call.
519 if((offpos = lseek(fd, 0, SEEK_END)) == -1) {
520 DEBUG(0, ("add_smbpwd_entry(lseek): Failed to add entry for user %s to file %s. \
521 Error was %s\n", pwd->smb_name, pfile, strerror(errno)));
526 new_entry_length = strlen(pwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + 2;
528 if((new_entry = (char *)malloc( new_entry_length )) == NULL) {
529 DEBUG(0, ("add_smbpwd_entry(malloc): Failed to add entry for user %s to file %s. \
530 Error was %s\n", pwd->smb_name, pfile, strerror(errno)));
535 sprintf(new_entry, "%s:%u:", pwd->smb_name, (unsigned)pwd->smb_userid);
536 p = (unsigned char *)&new_entry[strlen(new_entry)];
538 for( i = 0; i < 16; i++) {
539 sprintf((char *)&p[i*2], "%02X", pwd->smb_passwd[i]);
546 for( i = 0; i < 16; i++) {
547 sprintf((char *)&p[i*2], "%02X", pwd->smb_nt_passwd[i]);
552 sprintf((char *)p,"\n");
554 #ifdef DEBUG_PASSWORD
555 DEBUG(100, ("add_smbpwd_entry(%d): new_entry_len %d entry_len %d made line |%s|\n",
556 fd, new_entry_length, strlen(new_entry), new_entry));
559 if ((wr_len = write(fd, new_entry, strlen(new_entry))) != strlen(new_entry)) {
560 DEBUG(0, ("add_smbpwd_entry(write): %d Failed to add entry for user %s to file %s. \
561 Error was %s\n", wr_len, pwd->smb_name, pfile, strerror(errno)));
563 /* Remove the entry we just wrote. */
564 if(ftruncate(fd, offpos) == -1) {
565 DEBUG(0, ("add_smbpwd_entry: ERROR failed to ftruncate file %s. \
566 Error was %s. Password file may be corrupt ! Please examine by hand !\n",
567 pwd->smb_name, strerror(errno)));
578 /************************************************************************
579 Routine to search the smbpasswd file for an entry matching the username.
580 and then modify its password entry. We can't use the startsmbpwent()/
581 getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
582 in the actual file to decide how much room we have to write data.
583 ************************************************************************/
585 BOOL mod_smbpwd_entry(struct smb_passwd* pwd)
587 /* Static buffers we will return. */
588 static pstring user_name;
591 char readbuf[16 * 1024];
594 unsigned char *p = NULL;
595 long linebuf_len = 0;
598 char *pfile = lp_smb_passwd_file();
599 BOOL found_entry = False;
601 long pwd_seekpos = 0;
608 DEBUG(0, ("No SMB password file set\n"));
611 DEBUG(10, ("mod_smbpwd_entry: opening file %s\n", pfile));
613 fp = fopen(pfile, "r+");
616 DEBUG(0, ("mod_smbpwd_entry: unable to open file %s\n", pfile));
619 /* Set a 16k buffer to do more efficient reads */
620 setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
622 if ((lockfd = pw_file_lock(fileno(fp), F_RDLCK | F_WRLCK, 5)) < 0) {
623 DEBUG(0, ("mod_smbpwd_entry: unable to lock file %s\n", pfile));
628 /* Make sure it is only rw by the owner */
631 /* We have a write lock on the file. */
633 * Scan the file, a line at a time and check if the name matches.
636 pwd_seekpos = ftell(fp);
640 fgets(linebuf, 256, fp);
643 pw_file_unlock(lockfd);
648 * Check if the string is terminated with a newline - if not
649 * then we must keep reading and discard until we get one.
651 linebuf_len = strlen(linebuf);
652 if (linebuf[linebuf_len - 1] != '\n') {
654 while (!ferror(fp) && !feof(fp)) {
661 linebuf[linebuf_len - 1] = '\0';
664 #ifdef DEBUG_PASSWORD
665 DEBUG(100, ("mod_smbpwd_entry: got line |%s|\n", linebuf));
668 if ((linebuf[0] == 0) && feof(fp)) {
669 DEBUG(4, ("mod_smbpwd_entry: end of file reached\n"));
674 * The line we have should be of the form :-
676 * username:uid:[32hex bytes]:....other flags presently
681 * username:uid:[32hex bytes]:[32hex bytes]:....ignored....
683 * if Windows NT compatible passwords are also present.
686 if (linebuf[0] == '#' || linebuf[0] == '\0') {
687 DEBUG(6, ("mod_smbpwd_entry: skipping comment or blank line\n"));
691 p = (unsigned char *) strchr(linebuf, ':');
694 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (no :)\n"));
699 * As 256 is shorter than a pstring we don't need to check
700 * length here - if this ever changes....
702 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
703 user_name[PTR_DIFF(p, linebuf)] = '\0';
704 if (strequal(user_name, pwd->smb_name)) {
710 if (!found_entry) return False;
712 DEBUG(6, ("mod_smbpwd_entry: entry exists\n"));
714 /* User name matches - get uid and password */
715 p++; /* Go past ':' */
718 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (uid not number)\n"));
720 pw_file_unlock(lockfd);
724 while (*p && isdigit(*p))
727 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (no : after uid)\n"));
729 pw_file_unlock(lockfd);
734 * Now get the password value - this should be 32 hex digits
735 * which are the ascii representations of a 16 byte string.
736 * Get two at a time and put them into the password.
740 /* Record exact password position */
741 pwd_seekpos += PTR_DIFF(p, linebuf);
743 if (*p == '*' || *p == 'X') {
744 /* Password deliberately invalid - end here. */
745 DEBUG(10, ("get_smbpwd_entry: entry invalidated for user %s\n", user_name));
747 pw_file_unlock(lockfd);
751 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
752 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (passwd too short)\n"));
754 pw_file_unlock(lockfd);
759 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (no terminating :)\n"));
761 pw_file_unlock(lockfd);
765 if (*p == '*' || *p == 'X') {
767 pw_file_unlock(lockfd);
771 if (!strncasecmp((char *) p, "NO PASSWORD", 11)) {
773 pw_file_unlock(lockfd);
777 /* Now check if the NT compatible password is
779 p += 33; /* Move to the first character of the line after
780 the lanman password. */
781 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
782 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (passwd too short)\n"));
784 pw_file_unlock(lockfd);
789 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (no terminating :)\n"));
791 pw_file_unlock(lockfd);
795 /* Entry is correctly formed. */
798 * Do an atomic write into the file at the position defined by
802 /* The mod user write needs to be atomic - so get the fd from
803 the fp and do a raw write() call.
808 if (lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
809 DEBUG(0, ("mod_smbpwd_entry: seek fail on file %s.\n", pfile));
811 pw_file_unlock(lockfd);
815 /* Sanity check - ensure the character is a ':' */
816 if (read(fd, &c, 1) != 1) {
817 DEBUG(0, ("mod_smbpwd_entry: read fail on file %s.\n", pfile));
819 pw_file_unlock(lockfd);
824 DEBUG(0, ("mod_smbpwd_entry: check on passwd file %s failed.\n", pfile));
826 pw_file_unlock(lockfd);
830 /* Create the 32 byte representation of the new p16 */
831 for (i = 0; i < 16; i++) {
832 sprintf(&ascii_p16[i*2], "%02X", (uchar) pwd->smb_passwd[i]);
835 /* Add on the NT md4 hash */
838 if (pwd->smb_nt_passwd != NULL) {
839 for (i = 0; i < 16; i++) {
840 sprintf(&ascii_p16[(i*2)+33], "%02X", (uchar) pwd->smb_nt_passwd[i]);
843 /* No NT hash - write out an 'invalid' string. */
844 strcpy(&ascii_p16[33], "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
847 #ifdef DEBUG_PASSWORD
848 DEBUG(100,("mod_smbpwd_entry: "));
849 dump_data(100, ascii_p16, wr_len);
852 if (write(fd, ascii_p16, wr_len) != wr_len) {
853 DEBUG(0, ("mod_smbpwd_entry: write failed in passwd file %s\n", pfile));
855 pw_file_unlock(lockfd);
860 pw_file_unlock(lockfd);