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"));
190 pw_buf.acct_ctrl = ACB_NORMAL;
193 * Scan the file, a line at a time and check if the name matches.
198 fgets(linebuf, 256, fp);
204 * Check if the string is terminated with a newline - if not
205 * then we must keep reading and discard until we get one.
207 linebuf_len = strlen(linebuf);
208 if (linebuf[linebuf_len - 1] != '\n') {
210 while (!ferror(fp) && !feof(fp)) {
216 linebuf[linebuf_len - 1] = '\0';
218 #ifdef DEBUG_PASSWORD
219 DEBUG(100, ("getsmbpwent: got line |%s|\n", linebuf));
221 if ((linebuf[0] == 0) && feof(fp)) {
222 DEBUG(4, ("getsmbpwent: end of file reached\n"));
226 * The line we have should be of the form :-
228 * username:uid:[32hex bytes]:....other flags presently
233 * username:uid:[32hex bytes]:[32hex bytes]:....ignored....
235 * if Windows NT compatible passwords are also present.
238 if (linebuf[0] == '#' || linebuf[0] == '\0') {
239 DEBUG(6, ("getsmbpwent: skipping comment or blank line\n"));
242 p = (unsigned char *) strchr(linebuf, ':');
244 DEBUG(0, ("getsmbpwent: malformed password entry (no :)\n"));
248 * As 256 is shorter than a pstring we don't need to check
249 * length here - if this ever changes....
251 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
252 user_name[PTR_DIFF(p, linebuf)] = '\0';
256 p++; /* Go past ':' */
258 DEBUG(0, ("getsmbpwent: malformed password entry (uid not number)\n"));
262 uidval = atoi((char *) p);
264 while (*p && isdigit(*p))
268 DEBUG(0, ("getsmbpwent: malformed password entry (no : after uid)\n"));
272 pw_buf.smb_name = user_name;
273 pw_buf.smb_userid = uidval;
276 * Now get the password value - this should be 32 hex digits
277 * which are the ascii representations of a 16 byte string.
278 * Get two at a time and put them into the password.
284 if (*p == '*' || *p == 'X') {
285 /* Password deliberately invalid - end here. */
286 DEBUG(10, ("getsmbpwent: entry invalidated for user %s\n", user_name));
287 pw_buf.smb_nt_passwd = NULL;
288 pw_buf.smb_passwd = NULL;
289 pw_buf.acct_ctrl |= ACB_DISABLED;
293 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
294 DEBUG(0, ("getsmbpwent: malformed password entry (passwd too short)\n"));
299 DEBUG(0, ("getsmbpwent: malformed password entry (no terminating :)\n"));
303 if (!strncasecmp((char *) p, "NO PASSWORD", 11)) {
304 pw_buf.smb_passwd = NULL;
305 pw_buf.acct_ctrl |= ACB_PWNOTREQ;
307 if (!gethexpwd((char *)p, (char *)smbpwd)) {
308 DEBUG(0, ("getsmbpwent: Malformed Lanman password entry (non hex chars)\n"));
311 pw_buf.smb_passwd = smbpwd;
315 * Now check if the NT compatible password is
318 pw_buf.smb_nt_passwd = NULL;
320 p += 33; /* Move to the first character of the line after
321 the lanman password. */
322 if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
323 if (*p != '*' && *p != 'X') {
324 if(gethexpwd((char *)p,(char *)smbntpwd))
325 pw_buf.smb_nt_passwd = smbntpwd;
327 p += 33; /* Move to the first character of the line after
331 DEBUG(5, ("getsmbpwent: returning passwd entry for user %s, uid %d\n",
335 * Check if the account type bits have been encoded after the
336 * NT password (in the form [NDHTUWSLXI]).
340 BOOL finished = False;
342 pw_buf.acct_ctrl = 0;
344 for(p++;*p && !finished; p++) {
348 * Hmmm. Don't allow these to be set/read independently
349 * of the actual password fields. We don't want a mismatch.
354 pw_buf.acct_ctrl |= ACB_PWNOTREQ;
358 pw_buf.acct_ctrl |= ACB_DISABLED;
362 /* 'H'omedir required. */
363 pw_buf.acct_ctrl |= ACB_HOMDIRREQ;
366 /* 'T'emp account. */
367 pw_buf.acct_ctrl |= ACB_TEMPDUP;
370 /* 'U'ser account (normal). */
371 pw_buf.acct_ctrl |= ACB_NORMAL;
374 /* 'M'NS logon user account. What is this ? */
375 pw_buf.acct_ctrl |= ACB_MNS;
378 /* 'W'orkstation account. */
379 pw_buf.acct_ctrl |= ACB_WSTRUST;
382 /* 'S'erver account. */
383 pw_buf.acct_ctrl |= ACB_SVRTRUST;
386 /* 'L'ocked account. */
387 pw_buf.acct_ctrl |= ACB_AUTOLOCK;
391 pw_buf.acct_ctrl |= ACB_PWNOEXP;
394 /* 'I'nterdomain trust account. */
395 pw_buf.acct_ctrl |= ACB_DOMTRUST;
407 /* Must have some account type set. */
408 if(pw_buf.acct_ctrl == 0)
409 pw_buf.acct_ctrl = ACB_NORMAL;
412 /* 'Old' style file. Fake up based on user name. */
414 * Currently machine accounts are kept in the same
415 * password file as 'normal accounts'. If this changes
416 * we will have to fix this code. JRA.
418 if(pw_buf.smb_name[strlen(pw_buf.smb_name) - 1] == '$') {
419 pw_buf.acct_ctrl &= ~ACB_NORMAL;
420 pw_buf.acct_ctrl |= ACB_WSTRUST;
427 DEBUG(5,("getsmbpwent: end of file reached.\n"));
431 /*************************************************************************
432 Routine to search the smbpasswd file for an entry matching the username
433 or user id. if the name is NULL, then the smb_uid is used instead.
434 *************************************************************************/
436 struct smb_passwd *get_smbpwd_entry(char *name, int smb_userid)
438 struct smb_passwd *pwd = NULL;
442 DEBUG(10, ("get_smbpwd_entry: search by name: %s\n", name));
444 DEBUG(10, ("get_smbpwd_entry: search by smb_userid: %x\n", smb_userid));
447 /* Open the smbpassword file - not for update. */
448 fp = startsmbpwent(False);
451 DEBUG(0, ("get_smbpwd_entry: unable to open password file.\n"));
456 * Scan the file, a line at a time and check if the name
460 while ((pwd = getsmbpwent(fp)) != NULL) {
462 /* Search is by user name */
463 if (!strequal(pwd->smb_name, name))
465 DEBUG(10, ("get_smbpwd_entry: found by name: %s\n", name));
468 /* Search is by user id */
469 if (pwd->smb_userid != smb_userid)
471 DEBUG(10, ("get_smbpwd_entry: found by smb_userid: %x\n", smb_userid));
480 /************************************************************************
481 Routine to add an entry to the smbpasswd file.
482 *************************************************************************/
484 BOOL add_smbpwd_entry(struct smb_passwd *newpwd)
486 char *pfile = lp_smb_passwd_file();
487 struct smb_passwd *pwd = NULL;
494 int new_entry_length;
499 /* Open the smbpassword file - for update. */
500 fp = startsmbpwent(True);
503 DEBUG(0, ("add_smbpwd_entry: unable to open file.\n"));
508 * Scan the file, a line at a time and check if the name matches.
511 while ((pwd = getsmbpwent(fp)) != NULL) {
512 if (strequal(newpwd->smb_name, pwd->smb_name)) {
513 DEBUG(0, ("add_smbpwd_entry: entry with name %s already exists\n", pwd->smb_name));
519 /* Ok - entry doesn't exist. We can add it */
521 /* Create a new smb passwd entry and set it to the given password. */
523 * The add user write needs to be atomic - so get the fd from
524 * the fp and do a raw write() call.
528 if((offpos = lseek(fd, 0, SEEK_END)) == -1) {
529 DEBUG(0, ("add_smbpwd_entry(lseek): Failed to add entry for user %s to file %s. \
530 Error was %s\n", pwd->smb_name, pfile, strerror(errno)));
535 new_entry_length = strlen(pwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + 2;
537 if((new_entry = (char *)malloc( new_entry_length )) == NULL) {
538 DEBUG(0, ("add_smbpwd_entry(malloc): Failed to add entry for user %s to file %s. \
539 Error was %s\n", pwd->smb_name, pfile, strerror(errno)));
544 sprintf(new_entry, "%s:%u:", pwd->smb_name, (unsigned)pwd->smb_userid);
545 p = (unsigned char *)&new_entry[strlen(new_entry)];
547 for( i = 0; i < 16; i++) {
548 sprintf((char *)&p[i*2], "%02X", pwd->smb_passwd[i]);
555 for( i = 0; i < 16; i++) {
556 sprintf((char *)&p[i*2], "%02X", pwd->smb_nt_passwd[i]);
561 sprintf((char *)p,"\n");
563 #ifdef DEBUG_PASSWORD
564 DEBUG(100, ("add_smbpwd_entry(%d): new_entry_len %d entry_len %d made line |%s|\n",
565 fd, new_entry_length, strlen(new_entry), new_entry));
568 if ((wr_len = write(fd, new_entry, strlen(new_entry))) != strlen(new_entry)) {
569 DEBUG(0, ("add_smbpwd_entry(write): %d Failed to add entry for user %s to file %s. \
570 Error was %s\n", wr_len, pwd->smb_name, pfile, strerror(errno)));
572 /* Remove the entry we just wrote. */
573 if(ftruncate(fd, offpos) == -1) {
574 DEBUG(0, ("add_smbpwd_entry: ERROR failed to ftruncate file %s. \
575 Error was %s. Password file may be corrupt ! Please examine by hand !\n",
576 pwd->smb_name, strerror(errno)));
587 /************************************************************************
588 Routine to search the smbpasswd file for an entry matching the username.
589 and then modify its password entry. We can't use the startsmbpwent()/
590 getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
591 in the actual file to decide how much room we have to write data.
592 ************************************************************************/
594 BOOL mod_smbpwd_entry(struct smb_passwd* pwd)
596 /* Static buffers we will return. */
597 static pstring user_name;
600 char readbuf[16 * 1024];
603 unsigned char *p = NULL;
604 long linebuf_len = 0;
607 char *pfile = lp_smb_passwd_file();
608 BOOL found_entry = False;
610 long pwd_seekpos = 0;
617 DEBUG(0, ("No SMB password file set\n"));
620 DEBUG(10, ("mod_smbpwd_entry: opening file %s\n", pfile));
622 fp = fopen(pfile, "r+");
625 DEBUG(0, ("mod_smbpwd_entry: unable to open file %s\n", pfile));
628 /* Set a 16k buffer to do more efficient reads */
629 setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
631 if ((lockfd = pw_file_lock(fileno(fp), F_RDLCK | F_WRLCK, 5)) < 0) {
632 DEBUG(0, ("mod_smbpwd_entry: unable to lock file %s\n", pfile));
637 /* Make sure it is only rw by the owner */
640 /* We have a write lock on the file. */
642 * Scan the file, a line at a time and check if the name matches.
645 pwd_seekpos = ftell(fp);
649 fgets(linebuf, 256, fp);
652 pw_file_unlock(lockfd);
657 * Check if the string is terminated with a newline - if not
658 * then we must keep reading and discard until we get one.
660 linebuf_len = strlen(linebuf);
661 if (linebuf[linebuf_len - 1] != '\n') {
663 while (!ferror(fp) && !feof(fp)) {
670 linebuf[linebuf_len - 1] = '\0';
673 #ifdef DEBUG_PASSWORD
674 DEBUG(100, ("mod_smbpwd_entry: got line |%s|\n", linebuf));
677 if ((linebuf[0] == 0) && feof(fp)) {
678 DEBUG(4, ("mod_smbpwd_entry: end of file reached\n"));
683 * The line we have should be of the form :-
685 * username:uid:[32hex bytes]:....other flags presently
690 * username:uid:[32hex bytes]:[32hex bytes]:....ignored....
692 * if Windows NT compatible passwords are also present.
695 if (linebuf[0] == '#' || linebuf[0] == '\0') {
696 DEBUG(6, ("mod_smbpwd_entry: skipping comment or blank line\n"));
700 p = (unsigned char *) strchr(linebuf, ':');
703 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (no :)\n"));
708 * As 256 is shorter than a pstring we don't need to check
709 * length here - if this ever changes....
711 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
712 user_name[PTR_DIFF(p, linebuf)] = '\0';
713 if (strequal(user_name, pwd->smb_name)) {
719 if (!found_entry) return False;
721 DEBUG(6, ("mod_smbpwd_entry: entry exists\n"));
723 /* User name matches - get uid and password */
724 p++; /* Go past ':' */
727 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (uid not number)\n"));
729 pw_file_unlock(lockfd);
733 while (*p && isdigit(*p))
736 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (no : after uid)\n"));
738 pw_file_unlock(lockfd);
743 * Now get the password value - this should be 32 hex digits
744 * which are the ascii representations of a 16 byte string.
745 * Get two at a time and put them into the password.
749 /* Record exact password position */
750 pwd_seekpos += PTR_DIFF(p, linebuf);
752 if (*p == '*' || *p == 'X') {
753 /* Password deliberately invalid - end here. */
754 DEBUG(10, ("get_smbpwd_entry: entry invalidated for user %s\n", user_name));
756 pw_file_unlock(lockfd);
760 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
761 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (passwd too short)\n"));
763 pw_file_unlock(lockfd);
768 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (no terminating :)\n"));
770 pw_file_unlock(lockfd);
774 if (*p == '*' || *p == 'X') {
776 pw_file_unlock(lockfd);
780 /* Now check if the NT compatible password is
782 p += 33; /* Move to the first character of the line after
783 the lanman password. */
784 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
785 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (passwd too short)\n"));
787 pw_file_unlock(lockfd);
792 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (no terminating :)\n"));
794 pw_file_unlock(lockfd);
798 /* Entry is correctly formed. */
801 * Do an atomic write into the file at the position defined by
805 /* The mod user write needs to be atomic - so get the fd from
806 the fp and do a raw write() call.
811 if (lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
812 DEBUG(0, ("mod_smbpwd_entry: seek fail on file %s.\n", pfile));
814 pw_file_unlock(lockfd);
818 /* Sanity check - ensure the character is a ':' */
819 if (read(fd, &c, 1) != 1) {
820 DEBUG(0, ("mod_smbpwd_entry: read fail on file %s.\n", pfile));
822 pw_file_unlock(lockfd);
827 DEBUG(0, ("mod_smbpwd_entry: check on passwd file %s failed.\n", pfile));
829 pw_file_unlock(lockfd);
833 /* Create the 32 byte representation of the new p16 */
834 for (i = 0; i < 16; i++) {
835 sprintf(&ascii_p16[i*2], "%02X", (uchar) pwd->smb_passwd[i]);
838 /* Add on the NT md4 hash */
841 if (pwd->smb_nt_passwd != NULL) {
842 for (i = 0; i < 16; i++) {
843 sprintf(&ascii_p16[(i*2)+33], "%02X", (uchar) pwd->smb_nt_passwd[i]);
846 /* No NT hash - write out an 'invalid' string. */
847 strcpy(&ascii_p16[33], "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
850 #ifdef DEBUG_PASSWORD
851 DEBUG(100,("mod_smbpwd_entry: "));
852 dump_data(100, ascii_p16, wr_len);
855 if (write(fd, ascii_p16, wr_len) != wr_len) {
856 DEBUG(0, ("mod_smbpwd_entry: write failed in passwd file %s\n", pfile));
858 pw_file_unlock(lockfd);
863 pw_file_unlock(lockfd);