4 * Unix SMB/Netbios implementation. Version 1.9. smbpasswd module. Copyright
5 * (C) Jeremy Allison 1995-1997.
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 675
19 * Mass Ave, Cambridge, MA 02139, USA.
25 /* Static buffers we will return. */
26 static struct smb_passwd pw_buf
;
27 static pstring user_name
;
28 static unsigned char smbpwd
[16];
29 static unsigned char smbntpwd
[16];
31 static int gethexpwd(char *p
, char *pwd
)
34 unsigned char lonybble
, hinybble
;
35 char *hexchars
= "0123456789ABCDEF";
37 for (i
= 0; i
< 32; i
+= 2) {
38 hinybble
= toupper(p
[i
]);
39 lonybble
= toupper(p
[i
+ 1]);
41 p1
= strchr(hexchars
, hinybble
);
42 p2
= strchr(hexchars
, lonybble
);
46 hinybble
= PTR_DIFF(p1
, hexchars
);
47 lonybble
= PTR_DIFF(p2
, hexchars
);
49 pwd
[i
/ 2] = (hinybble
<< 4) | lonybble
;
55 _my_get_smbpwnam(FILE * fp
, char *name
, BOOL
* valid_old_pwd
,
56 BOOL
*got_valid_nt_entry
, long *pwd_seekpos
)
65 * Scan the file, a line at a time and check if the name matches.
69 *pwd_seekpos
= ftell(fp
);
71 fgets(linebuf
, 256, fp
);
76 * Check if the string is terminated with a newline - if not
77 * then we must keep reading and discard until we get one.
79 linebuf_len
= strlen(linebuf
);
80 if (linebuf
[linebuf_len
- 1] != '\n') {
82 while (!ferror(fp
) && !feof(fp
)) {
88 linebuf
[linebuf_len
- 1] = '\0';
90 if ((linebuf
[0] == 0) && feof(fp
))
93 * The line we have should be of the form :-
95 * username:uid:[32hex bytes]:....other flags presently
100 * username:uid:[32hex bytes]:[32hex bytes]:....ignored....
102 * if Windows NT compatible passwords are also present.
105 if (linebuf
[0] == '#' || linebuf
[0] == '\0')
107 p
= (unsigned char *) strchr(linebuf
, ':');
111 * As 256 is shorter than a pstring we don't need to check
112 * length here - if this ever changes....
114 strncpy(user_name
, linebuf
, PTR_DIFF(p
, linebuf
));
115 user_name
[PTR_DIFF(p
, linebuf
)] = '\0';
116 if (!strequal(user_name
, name
))
119 /* User name matches - get uid and password */
120 p
++; /* Go past ':' */
124 uidval
= atoi((char *) p
);
125 while (*p
&& isdigit(*p
))
132 * Now get the password value - this should be 32 hex digits
133 * which are the ascii representations of a 16 byte string.
134 * Get two at a time and put them into the password.
137 *pwd_seekpos
+= PTR_DIFF(p
, linebuf
); /* Save exact position
138 * of passwd in file -
141 if (*p
== '*' || *p
== 'X') {
142 /* Password deliberately invalid - end here. */
143 *valid_old_pwd
= False
;
144 *got_valid_nt_entry
= False
;
145 pw_buf
.smb_nt_passwd
= NULL
; /* No NT password (yet)*/
147 /* Now check if the NT compatible password is
149 p
+= 33; /* Move to the first character of the line after
150 the lanman password. */
151 if ((linebuf_len
>= (PTR_DIFF(p
, linebuf
) + 33)) && (p
[32] == ':')) {
152 /* NT Entry was valid - even if 'X' or '*', can be overwritten */
153 *got_valid_nt_entry
= True
;
154 if (*p
!= '*' && *p
!= 'X') {
155 if (gethexpwd((char *)p
,(char *)smbntpwd
))
156 pw_buf
.smb_nt_passwd
= smbntpwd
;
159 pw_buf
.smb_name
= user_name
;
160 pw_buf
.smb_userid
= uidval
;
161 pw_buf
.smb_passwd
= NULL
; /* No password */
164 if (linebuf_len
< (PTR_DIFF(p
, linebuf
) + 33))
170 if (!strncasecmp((char *)p
, "NO PASSWORD", 11)) {
171 pw_buf
.smb_passwd
= NULL
; /* No password */
173 if(!gethexpwd((char *)p
,(char *)smbpwd
))
175 pw_buf
.smb_passwd
= smbpwd
;
178 pw_buf
.smb_name
= user_name
;
179 pw_buf
.smb_userid
= uidval
;
180 pw_buf
.smb_nt_passwd
= NULL
;
181 *got_valid_nt_entry
= False
;
182 *valid_old_pwd
= True
;
184 /* Now check if the NT compatible password is
186 p
+= 33; /* Move to the first character of the line after
187 the lanman password. */
188 if ((linebuf_len
>= (PTR_DIFF(p
, linebuf
) + 33)) && (p
[32] == ':')) {
189 /* NT Entry was valid - even if 'X' or '*', can be overwritten */
190 *got_valid_nt_entry
= True
;
191 if (*p
!= '*' && *p
!= 'X') {
192 if (gethexpwd((char *)p
,(char *)smbntpwd
))
193 pw_buf
.smb_nt_passwd
= smbntpwd
;
202 * Print command usage on stderr and die.
204 static void usage(char *name
)
206 fprintf(stderr
, "Usage is : %s [username]\n", name
);
210 int main(int argc
, char **argv
)
216 uchar old_nt_p16
[16];
219 uchar new_nt_p16
[16];
221 struct smb_passwd
*smb_pwent
;
223 BOOL valid_old_pwd
= False
;
224 BOOL got_valid_nt_entry
= False
;
229 int ret
, i
, err
, writelen
;
231 char *pfile
= SMB_PASSWD_FILE
;
232 char readbuf
[16 * 1024];
236 setup_logging(argv
[0],True
);
238 charset_initialise(0);
240 #ifndef DEBUG_PASSWORD
241 /* Check the effective uid */
242 if (geteuid() != 0) {
243 fprintf(stderr
, "%s: Must be setuid root.\n", argv
[0]);
248 /* Get the real uid */
251 /* Deal with usage problems */
253 /* As root we can change anothers password. */
254 if (argc
!= 1 && argc
!= 2)
256 } else if (argc
!= 1)
260 if (real_uid
== 0 && argc
== 2) {
261 /* If we are root we can change anothers password. */
262 strncpy(user_name
, argv
[1], sizeof(user_name
) - 1);
263 user_name
[sizeof(user_name
) - 1] = '\0';
264 pwd
= getpwnam(user_name
);
266 pwd
= getpwuid(real_uid
);
270 fprintf(stderr
, "%s: Unable to get UNIX password entry for user.\n", argv
[0]);
273 /* If we are root we don't ask for the old password. */
274 old_passwd
[0] = '\0';
276 p
= getpass("Old SMB password:");
277 strncpy(old_passwd
, p
, sizeof(fstring
));
278 old_passwd
[sizeof(fstring
)-1] = '\0';
280 new_passwd
[0] = '\0';
281 p
= getpass("New SMB password:");
282 strncpy(new_passwd
, p
, sizeof(fstring
));
283 new_passwd
[sizeof(fstring
)-1] = '\0';
284 p
= getpass("Retype new SMB password:");
285 if (strcmp(p
, new_passwd
)) {
286 fprintf(stderr
, "%s: Mismatch - password unchanged.\n", argv
[0]);
290 if (new_passwd
[0] == '\0') {
291 printf("Password not set\n");
295 /* Calculate the MD4 hash (NT compatible) of the old and new passwords */
296 memset(old_nt_p16
, '\0', 16);
297 E_md4hash((uchar
*)old_passwd
, old_nt_p16
);
299 memset(new_nt_p16
, '\0', 16);
300 E_md4hash((uchar
*) new_passwd
, new_nt_p16
);
302 /* Mangle the passwords into Lanman format */
303 old_passwd
[14] = '\0';
304 strupper(old_passwd
);
305 new_passwd
[14] = '\0';
306 strupper(new_passwd
);
309 * Calculate the SMB (lanman) hash functions of both old and new passwords.
312 memset(old_p16
, '\0', 16);
313 E_P16((uchar
*) old_passwd
, old_p16
);
315 memset(new_p16
, '\0', 16);
316 E_P16((uchar
*) new_passwd
, new_p16
);
319 * Open the smbpaswd file XXXX - we need to parse smb.conf to get the
322 if ((fp
= fopen(pfile
, "r+")) == NULL
) {
324 fprintf(stderr
, "%s: Failed to open password file %s.\n",
330 /* Set read buffer to 16k for effiecient reads */
331 setvbuf(fp
, readbuf
, _IOFBF
, sizeof(readbuf
));
333 /* make sure it is only rw by the owner */
336 /* Lock the smbpasswd file for write. */
337 if ((lockfd
= pw_file_lock(pfile
, F_WRLCK
, 5)) < 0) {
339 fprintf(stderr
, "%s: Failed to lock password file %s.\n",
346 /* Get the smb passwd entry for this user */
347 smb_pwent
= _my_get_smbpwnam(fp
, pwd
->pw_name
, &valid_old_pwd
,
348 &got_valid_nt_entry
, &seekpos
);
349 if (smb_pwent
== NULL
) {
350 fprintf(stderr
, "%s: Failed to find entry for user %s in file %s.\n",
351 argv
[0], pwd
->pw_name
, pfile
);
353 pw_file_unlock(lockfd
);
356 /* If we are root we don't need to check the old password. */
358 if ((valid_old_pwd
== False
) || (smb_pwent
->smb_passwd
== NULL
)) {
359 fprintf(stderr
, "%s: User %s is disabled, plase contact your administrator to enable it.\n", argv
[0], pwd
->pw_name
);
361 pw_file_unlock(lockfd
);
364 /* Check the old Lanman password */
365 if (memcmp(old_p16
, smb_pwent
->smb_passwd
, 16)) {
366 fprintf(stderr
, "%s: Couldn't change password.\n", argv
[0]);
368 pw_file_unlock(lockfd
);
371 /* Check the NT password if it exists */
372 if (smb_pwent
->smb_nt_passwd
!= NULL
) {
373 if (memcmp(old_nt_p16
, smb_pwent
->smb_nt_passwd
, 16)) {
374 fprintf(stderr
, "%s: Couldn't change password.\n", argv
[0]);
376 pw_file_unlock(lockfd
);
382 * If we get here either we were root or the old password checked out
385 /* Create the 32 byte representation of the new p16 */
386 for (i
= 0; i
< 16; i
++) {
387 sprintf(&ascii_p16
[i
* 2], "%02X", (uchar
) new_p16
[i
]);
389 if(got_valid_nt_entry
) {
390 /* Add on the NT md4 hash */
392 for (i
= 0; i
< 16; i
++) {
393 sprintf(&ascii_p16
[(i
* 2)+33], "%02X", (uchar
) new_nt_p16
[i
]);
397 * Do an atomic write into the file at the position defined by
401 ret
= lseek(pwfd
, seekpos
- 1, SEEK_SET
);
402 if (ret
!= seekpos
- 1) {
404 fprintf(stderr
, "%s: seek fail on file %s.\n",
409 pw_file_unlock(lockfd
);
412 /* Sanity check - ensure the character is a ':' */
413 if (read(pwfd
, &c
, 1) != 1) {
415 fprintf(stderr
, "%s: read fail on file %s.\n",
420 pw_file_unlock(lockfd
);
424 fprintf(stderr
, "%s: sanity check on passwd file %s failed.\n",
427 pw_file_unlock(lockfd
);
430 writelen
= (got_valid_nt_entry
) ? 65 : 32;
431 if (write(pwfd
, ascii_p16
, writelen
) != writelen
) {
433 fprintf(stderr
, "%s: write fail in file %s.\n",
438 pw_file_unlock(lockfd
);
442 pw_file_unlock(lockfd
);
443 printf("Password changed\n");
449 #include "includes.h"
452 main(int argc
, char **argv
)
454 printf("smb password encryption not selected in Makefile\n");