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;
33 do_pw_lock(int fd, int waitsecs, int type)
39 signal(SIGALRM, SIGNAL_CAST gotalarm_sig);
42 lock.l_whence = SEEK_SET;
48 ret = fcntl(fd, F_SETLKW, &lock);
50 signal(SIGALRM, SIGNAL_CAST SIG_DFL);
53 DEBUG(0, ("do_pw_lock: failed to %s SMB passwd file.\n",
54 type == F_UNLCK ? "unlock" : "lock"));
60 int pw_file_lock(char *name, int type, int secs)
62 int fd = open(name, O_RDWR | O_CREAT, 0600);
65 if (do_pw_lock(fd, secs, type)) {
72 int pw_file_unlock(int fd)
74 do_pw_lock(fd, 5, F_UNLCK);
79 * Routine to get the next 32 hex characters and turn them
80 * into a 16 byte array.
83 static int gethexpwd(char *p, char *pwd)
86 unsigned char lonybble, hinybble;
87 char *hexchars = "0123456789ABCDEF";
90 for (i = 0; i < 32; i += 2) {
91 hinybble = toupper(p[i]);
92 lonybble = toupper(p[i + 1]);
94 p1 = strchr(hexchars, hinybble);
95 p2 = strchr(hexchars, lonybble);
98 hinybble = PTR_DIFF(p1, hexchars);
99 lonybble = PTR_DIFF(p2, hexchars);
101 pwd[i / 2] = (hinybble << 4) | lonybble;
106 /*************************************************************************
107 Routine to search the smbpasswd file for an entry matching the username
108 or user id. if the name is NULL, then the smb_uid is used instead.
109 *************************************************************************/
110 struct smb_passwd *get_smbpwd_entry(char *name, int smb_userid)
112 /* Static buffers we will return. */
113 static struct smb_passwd pw_buf;
114 static pstring user_name;
115 static unsigned char smbpwd[16];
116 static unsigned char smbntpwd[16];
118 char readbuf[16 * 1024];
125 char *pfile = lp_smb_passwd_file();
128 DEBUG(0, ("No SMB password file set\n"));
131 DEBUG(10, ("get_smbpwd_entry: opening file %s\n", pfile));
135 DEBUG(10, ("get_smbpwd_entry: search by name: %s\n", name));
139 DEBUG(10, ("get_smbpwd_entry: search by smb_userid: %x\n", smb_userid));
142 fp = fopen(pfile, "r");
145 DEBUG(0, ("get_smbpwd_entry: unable to open file %s\n", pfile));
148 /* Set a 16k buffer to do more efficient reads */
149 setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
151 if ((lockfd = pw_file_lock(pfile, F_RDLCK, 5)) < 0) {
152 DEBUG(0, ("get_smbpwd_entry: unable to lock file %s\n", pfile));
156 /* make sure it is only rw by the owner */
159 /* We have a read lock on the file. */
161 * Scan the file, a line at a time and check if the name matches.
166 fgets(linebuf, 256, fp);
169 pw_file_unlock(lockfd);
173 * Check if the string is terminated with a newline - if not
174 * then we must keep reading and discard until we get one.
176 linebuf_len = strlen(linebuf);
177 if (linebuf[linebuf_len - 1] != '\n') {
179 while (!ferror(fp) && !feof(fp)) {
185 linebuf[linebuf_len - 1] = '\0';
187 #ifdef DEBUG_PASSWORD
188 DEBUG(100, ("get_smbpwd_entry: got line |%s|\n", linebuf));
190 if ((linebuf[0] == 0) && feof(fp)) {
191 DEBUG(4, ("get_smbpwd_entry: end of file reached\n"));
195 * The line we have should be of the form :-
197 * username:uid:[32hex bytes]:....other flags presently
202 * username:uid:[32hex bytes]:[32hex bytes]:....ignored....
204 * if Windows NT compatible passwords are also present.
207 if (linebuf[0] == '#' || linebuf[0] == '\0') {
208 DEBUG(6, ("get_smbpwd_entry: skipping comment or blank line\n"));
211 p = (unsigned char *) strchr(linebuf, ':');
213 DEBUG(0, ("get_smbpwd_entry: malformed password entry (no :)\n"));
217 * As 256 is shorter than a pstring we don't need to check
218 * length here - if this ever changes....
220 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
221 user_name[PTR_DIFF(p, linebuf)] = '\0';
225 p++; /* Go past ':' */
227 DEBUG(0, ("get_smbpwd_entry: malformed password entry (uid not number)\n"));
229 pw_file_unlock(lockfd);
233 uidval = atoi((char *) p);
235 while (*p && isdigit(*p))
242 DEBUG(0, ("get_smbpwd_entry: malformed password entry (no : after uid)\n"));
244 pw_file_unlock(lockfd);
250 /* search is by user name */
251 if (!strequal(user_name, name)) continue;
252 DEBUG(10, ("get_smbpwd_entry: found by name: %s\n", user_name));
256 /* search is by user id */
257 if (uidval != smb_userid) continue;
258 DEBUG(10, ("get_smbpwd_entry: found by smb_userid: %x\n", uidval));
261 /* if we're here, the entry has been found (either by name or uid) */
264 * Now get the password value - this should be 32 hex digits
265 * which are the ascii representations of a 16 byte string.
266 * Get two at a time and put them into the password.
272 if (*p == '*' || *p == 'X')
274 /* Password deliberately invalid - end here. */
275 DEBUG(10, ("get_smbpwd_entry: entry invalidated for user %s\n", user_name));
277 pw_file_unlock(lockfd);
281 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33))
283 DEBUG(0, ("get_smbpwd_entry: malformed password entry (passwd too short)\n"));
285 pw_file_unlock(lockfd);
291 DEBUG(0, ("get_smbpwd_entry: malformed password entry (no terminating :)\n"));
293 pw_file_unlock(lockfd);
297 if (!strncasecmp((char *) p, "NO PASSWORD", 11))
299 pw_buf.smb_passwd = NULL;
303 if (!gethexpwd((char *)p, (char *)smbpwd))
305 DEBUG(0, ("Malformed Lanman password entry (non hex chars)\n"));
307 pw_file_unlock(lockfd);
310 pw_buf.smb_passwd = smbpwd;
313 pw_buf.smb_name = user_name;
314 pw_buf.smb_userid = uidval;
315 pw_buf.smb_nt_passwd = NULL;
317 /* Now check if the NT compatible password is
319 p += 33; /* Move to the first character of the line after
320 the lanman password. */
321 if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
322 if (*p != '*' && *p != 'X') {
323 if(gethexpwd((char *)p,(char *)smbntpwd))
324 pw_buf.smb_nt_passwd = smbntpwd;
329 pw_file_unlock(lockfd);
330 DEBUG(5, ("get_smbpwd_entrye: returning passwd entry for user %s, uid %d\n",
336 pw_file_unlock(lockfd);
341 * Routine to search the smbpasswd file for an entry matching the username.
343 BOOL add_smbpwd_entry(struct smb_passwd* pwd)
345 /* Static buffers we will return. */
346 static pstring user_name;
349 char readbuf[16 * 1024];
355 char *pfile = lp_smb_passwd_file();
361 int new_entry_length;
367 DEBUG(0, ("No SMB password file set\n"));
370 DEBUG(10, ("add_smbpwd_entry: opening file %s\n", pfile));
372 fp = fopen(pfile, "r+");
376 DEBUG(0, ("add_smbpwd_entry: unable to open file %s\n", pfile));
379 /* Set a 16k buffer to do more efficient reads */
380 setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
382 if ((lockfd = pw_file_lock(pfile, F_RDLCK | F_WRLCK, 5)) < 0)
384 DEBUG(0, ("add_smbpwd_entry: unable to lock file %s\n", pfile));
388 /* make sure it is only rw by the owner */
391 /* We have a write lock on the file. */
393 * Scan the file, a line at a time and check if the name matches.
399 fgets(linebuf, 256, fp);
403 pw_file_unlock(lockfd);
408 * Check if the string is terminated with a newline - if not
409 * then we must keep reading and discard until we get one.
411 linebuf_len = strlen(linebuf);
412 if (linebuf[linebuf_len - 1] != '\n')
415 while (!ferror(fp) && !feof(fp))
426 linebuf[linebuf_len - 1] = '\0';
429 #ifdef DEBUG_PASSWORD
430 DEBUG(100, ("add_smbpwd_entry: got line |%s|\n", linebuf));
433 if ((linebuf[0] == 0) && feof(fp))
435 DEBUG(4, ("add_smbpwd_entry: end of file reached\n"));
440 * The line we have should be of the form :-
442 * username:uid:[32hex bytes]:....other flags presently
447 * username:uid:[32hex bytes]:[32hex bytes]:....ignored....
449 * if Windows NT compatible passwords are also present.
452 if (linebuf[0] == '#' || linebuf[0] == '\0')
454 DEBUG(6, ("add_smbpwd_entry: skipping comment or blank line\n"));
458 p = (unsigned char *) strchr(linebuf, ':');
462 DEBUG(0, ("add_smbpwd_entry: malformed password entry (no :)\n"));
467 * As 256 is shorter than a pstring we don't need to check
468 * length here - if this ever changes....
470 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
471 user_name[PTR_DIFF(p, linebuf)] = '\0';
472 if (strequal(user_name, pwd->smb_name))
474 DEBUG(6, ("add_smbpwd_entry: entry already exists\n"));
479 /* ok - entry doesn't exist. we can add it */
481 /* Create a new smb passwd entry and set it to the given password. */
482 /* The add user write needs to be atomic - so get the fd from
483 the fp and do a raw write() call.
487 if((offpos = lseek(fd, 0, SEEK_END)) == -1)
489 DEBUG(0, ("add_smbpwd_entry(lseek): Failed to add entry for user %s to file %s. \
490 Error was %s\n", pwd->smb_name, pfile, strerror(errno)));
493 pw_file_unlock(lockfd);
497 new_entry_length = strlen(pwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + 2;
499 if((new_entry = (char *)malloc( new_entry_length )) == 0)
501 DEBUG(0, ("add_smbpwd_entry(malloc): Failed to add entry for user %s to file %s. \
503 pwd->smb_name, pfile, strerror(errno)));
506 pw_file_unlock(lockfd);
510 sprintf(new_entry, "%s:%u:", pwd->smb_name, (unsigned)pwd->smb_userid);
511 p = (unsigned char *)&new_entry[strlen(new_entry)];
513 for( i = 0; i < 16; i++)
515 sprintf((char *)&p[i*2], "%02X", pwd->smb_passwd[i]);
521 for( i = 0; i < 16; i++)
523 sprintf((char *)&p[i*2], "%02X", pwd->smb_nt_passwd[i]);
528 sprintf((char *)p,"\n");
530 #ifdef DEBUG_PASSWORD
531 DEBUG(100, ("add_smbpwd_entry(%d): new_entry_len %d entry_len %d made line |%s|\n",
532 fd, new_entry_length, strlen(new_entry), new_entry));
535 if ((wr_len = write(fd, new_entry, strlen(new_entry))) != strlen(new_entry))
537 DEBUG(0, ("add_smbpwd_entry(write): %d Failed to add entry for user %s to file %s. \
538 Error was %s\n", wr_len, pwd->smb_name, pfile, strerror(errno)));
540 /* Remove the entry we just wrote. */
541 if(ftruncate(fd, offpos) == -1)
543 DEBUG(0, ("add_smbpwd_entry: ERROR failed to ftruncate file %s. \
544 Error was %s. Password file may be corrupt ! Please examine by hand !\n",
545 pwd->smb_name, strerror(errno)));
549 pw_file_unlock(lockfd);
554 pw_file_unlock(lockfd);
558 * Routine to search the smbpasswd file for an entry matching the username.
559 * and then modify its password entry
561 BOOL mod_smbpwd_entry(struct smb_passwd* pwd)
563 /* Static buffers we will return. */
564 static pstring user_name;
567 char readbuf[16 * 1024];
570 unsigned char *p = NULL;
571 long linebuf_len = 0;
574 char *pfile = lp_smb_passwd_file();
575 BOOL found_entry = False;
577 long pwd_seekpos = 0;
585 DEBUG(0, ("No SMB password file set\n"));
588 DEBUG(10, ("mod_smbpwd_entry: opening file %s\n", pfile));
590 fp = fopen(pfile, "r+");
594 DEBUG(0, ("mod_smbpwd_entry: unable to open file %s\n", pfile));
597 /* Set a 16k buffer to do more efficient reads */
598 setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
600 if ((lockfd = pw_file_lock(pfile, F_RDLCK | F_WRLCK, 5)) < 0)
602 DEBUG(0, ("mod_smbpwd_entry: unable to lock file %s\n", pfile));
606 /* make sure it is only rw by the owner */
609 /* We have a write lock on the file. */
611 * Scan the file, a line at a time and check if the name matches.
615 pwd_seekpos = ftell(fp);
619 fgets(linebuf, 256, fp);
623 pw_file_unlock(lockfd);
628 * Check if the string is terminated with a newline - if not
629 * then we must keep reading and discard until we get one.
631 linebuf_len = strlen(linebuf);
632 if (linebuf[linebuf_len - 1] != '\n')
635 while (!ferror(fp) && !feof(fp))
646 linebuf[linebuf_len - 1] = '\0';
649 #ifdef DEBUG_PASSWORD
650 DEBUG(100, ("mod_smbpwd_entry: got line |%s|\n", linebuf));
653 if ((linebuf[0] == 0) && feof(fp))
655 DEBUG(4, ("mod_smbpwd_entry: end of file reached\n"));
660 * The line we have should be of the form :-
662 * username:uid:[32hex bytes]:....other flags presently
667 * username:uid:[32hex bytes]:[32hex bytes]:....ignored....
669 * if Windows NT compatible passwords are also present.
672 if (linebuf[0] == '#' || linebuf[0] == '\0')
674 DEBUG(6, ("mod_smbpwd_entry: skipping comment or blank line\n"));
678 p = (unsigned char *) strchr(linebuf, ':');
682 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (no :)\n"));
687 * As 256 is shorter than a pstring we don't need to check
688 * length here - if this ever changes....
690 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
691 user_name[PTR_DIFF(p, linebuf)] = '\0';
692 if (strequal(user_name, pwd->smb_name))
699 if (!found_entry) return False;
701 DEBUG(6, ("mod_smbpwd_entry: entry exists\n"));
703 /* User name matches - get uid and password */
704 p++; /* Go past ':' */
708 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (uid not number)\n"));
710 pw_file_unlock(lockfd);
714 while (*p && isdigit(*p))
718 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (no : after uid)\n"));
720 pw_file_unlock(lockfd);
724 * Now get the password value - this should be 32 hex digits
725 * which are the ascii representations of a 16 byte string.
726 * Get two at a time and put them into the password.
730 /* record exact password position */
731 pwd_seekpos += PTR_DIFF(p, linebuf);
733 if (*p == '*' || *p == 'X')
735 /* Password deliberately invalid - end here. */
736 DEBUG(10, ("get_smbpwd_entry: entry invalidated for user %s\n", user_name));
738 pw_file_unlock(lockfd);
742 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33))
744 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (passwd too short)\n"));
746 pw_file_unlock(lockfd);
752 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (no terminating :)\n"));
754 pw_file_unlock(lockfd);
758 if (*p == '*' || *p == 'X')
761 pw_file_unlock(lockfd);
764 if (!strncasecmp((char *) p, "NO PASSWORD", 11))
767 pw_file_unlock(lockfd);
771 /* Now check if the NT compatible password is
773 p += 33; /* Move to the first character of the line after
774 the lanman password. */
775 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33))
777 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (passwd too short)\n"));
779 pw_file_unlock(lockfd);
785 DEBUG(0, ("mod_smbpwd_entry: malformed password entry (no terminating :)\n"));
787 pw_file_unlock(lockfd);
791 /* The following check is wrong - the NT hash is optional. */
793 if (*p == '*' || *p == 'X')
796 pw_file_unlock(lockfd);
801 /* whew. entry is correctly formed. */
804 * Do an atomic write into the file at the position defined by
808 /* The mod user write needs to be atomic - so get the fd from
809 the fp and do a raw write() call.
814 if (lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1)
816 DEBUG(1, ("mod_smbpwd_entry: seek fail on file %s.\n", pfile));
818 pw_file_unlock(lockfd);
822 /* Sanity check - ensure the character is a ':' */
823 if (read(fd, &c, 1) != 1)
825 DEBUG(1, ("mod_smbpwd_entry: read fail on file %s.\n", pfile));
827 pw_file_unlock(lockfd);
833 DEBUG(1, ("mod_smbpwd_entry: check on passwd file %s failed.\n", pfile));
835 pw_file_unlock(lockfd);
839 /* Create the 32 byte representation of the new p16 */
840 for (i = 0; i < 16; i++)
842 sprintf(&ascii_p16[i*2], "%02X", (uchar) pwd->smb_passwd[i]);
844 /* Add on the NT md4 hash */
847 if (pwd->smb_nt_passwd != NULL)
849 for (i = 0; i < 16; i++)
851 sprintf(&ascii_p16[(i*2)+33], "%02X", (uchar) pwd->smb_nt_passwd[i]);
856 /* No NT hash - write out an 'invalid' string. */
857 strcpy(&ascii_p16[33], "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
860 #ifdef DEBUG_PASSWORD
861 DEBUG(100,("mod_smbpwd_entry: "));
862 dump_data(100, ascii_p16, wr_len);
865 if (write(fd, ascii_p16, wr_len) != wr_len)
867 DEBUG(1, ("mod_smbpwd_entry: write failed in passwd file %s\n", pfile));
869 pw_file_unlock(lockfd);
874 pw_file_unlock(lockfd);