1 /*
2  * (C) Jeremy Allison 1997. All rights reserved.
3  *
4  * This program is free for commercial and non-commercial use.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted.
8  *
9  * THIS SOFTWARE IS PROVIDED BY JEREMY ALLISON ``AS IS'' AND
10  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
11  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
12  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
13  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
14  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
15  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
16  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
17  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
18  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
19  * SUCH DAMAGE.
20  *
21  */
22 
23 #include <windows.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 
28 #include "des.h"
29 
30 /*
31  * Program to dump the Lanman and NT MD4 Hashed passwords from
32  * an NT SAM database into a Samba smbpasswd file. Needs Administrator
33  * privillages to run.
34  * Takes one arg - the name of the machine whose SAM database you
35  * wish to dump, if this arg is not given it dumps the local machine
36  * account database.
37  */
38 
39 /*
40  * Convert system error to char. Returns
41  * memory allocated with LocalAlloc.
42  */
43 
error_to_string(DWORD error)44 char *error_to_string(DWORD error)
45 {
46   char *msgbuf;
47 
48   if(FormatMessage(
49        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
50        NULL,
51        error,
52        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
53        (char *)&msgbuf,
54        0,
55        NULL
56        ) == 0)
57     return 0;
58   return msgbuf;
59 }
60 
61 /*
62  * Return a pointer to a string describing an os error.
63  * error_to_string returns a pointer to LocalAlloc'ed
64  * memory. Cache it and release when the next one is
65  * requested.
66  */
67 
str_oserr(DWORD err)68 char *str_oserr(DWORD err)
69 {
70   static char *lastmsg = 0;
71 
72   if(lastmsg)
73     LocalFree((HLOCAL)lastmsg);
74 
75   lastmsg = error_to_string(err);
76   return lastmsg;
77 }
78 
79 /*
80  * Utility function to get allocate a SID from a name.
81  * Looks on local machine. SID is allocated with LocalAlloc
82  * and must be freed by the caller.
83  * Returns TRUE on success, FALSE on fail.
84  */
85 
get_sid(const char * name,SID ** ppsid)86 BOOL get_sid(const char *name, SID **ppsid)
87 {
88   SID_NAME_USE sid_use;
89   DWORD sid_size = 0;
90   DWORD dom_size = 0;
91   char *domain;
92 
93   *ppsid = 0;
94   if(LookupAccountName(0, name, 0, &sid_size, 0, &dom_size, &sid_use) == 0) {
95     if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
96       fprintf( stderr, "get_sid: LookupAccountName for size on name %s failed. Error was %s\n",
97             name, str_oserr(GetLastError()));
98       return FALSE;
99     }
100   }
101 
102   *ppsid = (SID *)LocalAlloc( LMEM_FIXED, sid_size);
103   domain = (char *)LocalAlloc( LMEM_FIXED, dom_size);
104   if( *ppsid == 0 || domain == 0) {
105     fprintf( stderr, "get_sid: LocalAlloc failed. Error was %s\n",
106                  str_oserr(GetLastError()));
107     if(*ppsid)
108       LocalFree((HLOCAL)*ppsid);
109     if(domain)
110       LocalFree((HLOCAL)domain);
111     *ppsid = 0;
112     return FALSE;
113   }
114 
115   if(LookupAccountName(0, name, *ppsid, &sid_size, domain, &dom_size, &sid_use) == 0) {
116     fprintf( stderr,
117          "get_sid: LookupAccountName failed for name %s. Error was %s\n",
118          name, str_oserr(GetLastError()));
119     LocalFree((HLOCAL)*ppsid);
120     LocalFree((HLOCAL)domain);
121     *ppsid = 0;
122     return FALSE;
123   }
124 
125   LocalFree((HLOCAL)domain);
126   return TRUE;
127 }
128 
129 /*
130  * Utility function to setup a security descriptor
131  * from a varargs list of char *name followed by a DWORD access
132  * mask. The access control list is allocated with LocalAlloc
133  * and must be freed by the caller.
134  * returns TRUE on success, FALSE on fail.
135  */
136 
create_sd_from_list(SECURITY_DESCRIPTOR * sdout,int num,...)137 BOOL create_sd_from_list( SECURITY_DESCRIPTOR *sdout, int num, ...)
138 {
139   va_list ap;
140   SID **sids = 0;
141   char *name;
142   DWORD amask;
143   DWORD acl_size;
144   PACL pacl = 0;
145   int i;
146 
147   if((sids = (SID **)calloc(1,sizeof(SID *)*num)) == 0) {
148     fprintf(stderr, "create_sd_from_list: calloc fail.\n");
149     return FALSE;
150   }
151 
152   acl_size = num * (sizeof(ACL) +
153              sizeof(ACCESS_ALLOWED_ACE) +
154              sizeof(DWORD));
155 
156   /* Collect all the SID's */
157   va_start( ap, num);
158   for( i = 0; i < num; i++) {
159     name = va_arg( ap, char *);
160     amask = va_arg(ap, DWORD);
161     if(get_sid( name, &sids[i]) == FALSE)
162       goto cleanup;
163     acl_size += GetLengthSid(sids[i]);
164   }
165   va_end(ap);
166   if((pacl = (PACL)LocalAlloc( LMEM_FIXED, acl_size)) == 0) {
167     fprintf( stderr, "create_sd_from_list: LocalAlloc fail. Error was %s\n",
168             str_oserr(GetLastError()));
169     goto cleanup;
170   }
171 
172   if(InitializeSecurityDescriptor( sdout, SECURITY_DESCRIPTOR_REVISION) == FALSE) {
173     fprintf( stderr, "create_sd_from_list: InitializeSecurityDescriptor fail. Error was %s\n",
174                  str_oserr(GetLastError()));
175     goto cleanup;
176   }
177   if(InitializeAcl( pacl, acl_size, ACL_REVISION) == FALSE) {
178     fprintf( stderr, "create_sd_from_list: InitializeAcl fail. Error was %s\n",
179                  str_oserr(GetLastError()));
180     goto cleanup;
181   }
182   va_start(ap, num);
183   for( i = 0; i < num; i++) {
184     ACE_HEADER *ace_p;
185     name = va_arg( ap, char *);
186     amask = va_arg( ap, DWORD);
187     if(AddAccessAllowedAce( pacl, ACL_REVISION, amask, sids[i]) == FALSE) {
188       fprintf( stderr, "create_sd_from_list: AddAccessAllowedAce fail. Error was %s\n",
189                  str_oserr(GetLastError()));
190       goto cleanup;
191     }
192     /* Make sure the ACE is inheritable */
193     if(GetAce( pacl, 0, (LPVOID *)&ace_p) == FALSE) {
194       fprintf( stderr, "create_sd_from_list: GetAce fail. Error was %s\n",
195                  str_oserr(GetLastError()));
196       goto cleanup;
197     }
198     ace_p->AceFlags |= ( CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE);
199   }
200 
201   /* Add the ACL into the sd. */
202   if(SetSecurityDescriptorDacl( sdout, TRUE, pacl, FALSE) == FALSE) {
203     fprintf( stderr, "create_sd_from_list: SetSecurityDescriptorDacl fail. Error was %s\n",
204                str_oserr(GetLastError()));
205     goto cleanup;
206   }
207   for( i = 0; i < num; i++)
208     if(sids[i] != 0)
209       LocalFree((HLOCAL)sids[i]);
210   free(sids);
211 
212   return TRUE;
213 
214 cleanup:
215 
216   if(sids != 0) {
217     for( i = 0; i < num; i++)
218       if(sids[i] != 0)
219         LocalFree((HLOCAL)sids[i]);
220     free(sids);
221   }
222   if(pacl != 0)
223     LocalFree((HLOCAL)pacl);
224   return FALSE;
225 }
226 
227 /*
228  * Function to go over all the users in the SAM and set an ACL
229  * on them.
230  */
231 
set_userkeys_security(HKEY start,const char * path,SECURITY_DESCRIPTOR * psd,HKEY * return_key)232 int set_userkeys_security( HKEY start, const char *path, SECURITY_DESCRIPTOR *psd,
233 						  HKEY *return_key)
234 {
235 	HKEY key;
236 	DWORD err;
237 	char usersid[128];
238 	DWORD indx = 0;
239 
240 	/* Open the path and enum all the user keys - setting
241 	   the same security on them. */
242 	if((err = RegOpenKeyEx( start, path, 0, KEY_ENUMERATE_SUB_KEYS, &key)) !=
243 					ERROR_SUCCESS) {
244 		fprintf(stderr, "set_userkeys_security: Failed to open key %s to enumerate. \
245 Error was %s.\n",
246 				    path, str_oserr(err));
247 			return -1;
248 	}
249 
250 
251 	/* Now enumerate the subkeys, setting the security on them all. */
252 	do {
253 		DWORD size;
254 		FILETIME ft;
255 
256 		size = sizeof(usersid);
257 		err = RegEnumKeyEx(	key, indx, usersid, &size, 0, 0, 0, &ft);
258 		if(err == ERROR_SUCCESS) {
259 			HKEY subkey;
260 
261 			indx++;
262 			if((err = RegOpenKeyEx( key, usersid, 0, WRITE_DAC, &subkey)) !=
263 						ERROR_SUCCESS) {
264 				fprintf(stderr, "set_userkeys_security: Failed to open key %s to set security. \
265 Error was %s.\n",
266 						usersid, str_oserr(err));
267 				RegCloseKey(key);
268 				return -1;
269 			}
270 			if((err = RegSetKeySecurity( subkey, DACL_SECURITY_INFORMATION,
271 										 psd)) != ERROR_SUCCESS) {
272 				fprintf(stderr, "set_userkeys_security: Failed to set security on key %s. \
273 Error was %s.\n",
274 						usersid, str_oserr(err));
275 				RegCloseKey(subkey);
276 				RegCloseKey(key);
277 				return -1;
278 			}
279 			RegCloseKey(subkey);
280 		}
281 	} while(err == ERROR_SUCCESS);
282 
283 	if(err != ERROR_NO_MORE_ITEMS) {
284 		RegCloseKey(key);
285 		return -1;
286 	}
287 	if(return_key == 0)
288 		RegCloseKey(key);
289 	else
290 		*return_key = key;
291 	return 0;
292 }
293 
294 /*
295  * Function to travel down the SAM security tree in the registry and restore
296  * the correct ACL on them. Returns 0 on success. -1 on fail.
297  */
298 
restore_sam_tree_access(HKEY start)299 int restore_sam_tree_access( HKEY start )
300 {
301 	char path[128];
302 	char *p;
303 	HKEY key;
304 	DWORD err;
305 	SECURITY_DESCRIPTOR sd;
306 	DWORD admin_mask;
307 
308 	admin_mask = WRITE_DAC | READ_CONTROL;
309 
310 	if(create_sd_from_list( &sd, 2, "SYSTEM", GENERIC_ALL,
311 							"Administrators", admin_mask) == FALSE)
312 		return -1;
313 
314 	strcpy( path, "SECURITY\\SAM\\Domains\\Account\\Users");
315 
316 	/* Remove the security on the user keys first. */
317 	if(set_userkeys_security( start, path, &sd, 0) != 0)
318 			return -1;
319 
320 	/* now go up the path, restoring security */
321 	do {
322 		if((err = RegOpenKeyEx( start, path, 0, WRITE_DAC, &key)) !=
323 						ERROR_SUCCESS) {
324 			fprintf(stderr, "restore_sam_tree_access:Failed to open key %s to set \
325 security. Error was %s.\n",
326 					path, str_oserr(err));
327 			return -1;
328 		}
329 		if((err = RegSetKeySecurity( key, DACL_SECURITY_INFORMATION,
330 									 &sd)) != ERROR_SUCCESS) {
331 			fprintf(stderr, "restore_sam_tree_access: Failed to set security on key %s. \
332 Error was %s.\n",
333 					path, str_oserr(err));
334 			RegCloseKey(key);
335 			return  -1;
336 		}
337 		RegCloseKey(key);
338 		p = strrchr(path, '\\');
339 		if( p != 0)
340 			*p = 0;
341 	} while( p != 0 );
342 
343 	return 0;
344 }
345 
346 /*
347  * Function to travel the security tree and add Administrators
348  * access as WRITE_DAC, READ_CONTROL and READ.
349  * Returns 0 on success. -1 on fail if no security was changed,
350  * -2 on fail if security was changed.
351  */
352 
set_sam_tree_access(HKEY start,HKEY * return_key)353 int set_sam_tree_access( HKEY start, HKEY *return_key)
354 {
355 	char path[128];
356 	char *p;
357 	HKEY key;
358 	DWORD err;
359 	BOOL security_changed = FALSE;
360 	SECURITY_DESCRIPTOR sd;
361 	DWORD admin_mask;
362 	BOOL finished = FALSE;
363 
364 	admin_mask = WRITE_DAC | READ_CONTROL | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS;
365 
366 	if(create_sd_from_list( &sd, 2, "SYSTEM", GENERIC_ALL,
367 							"Administrators", admin_mask) == FALSE)
368 		return -1;
369 
370 	strcpy( path, "SECURITY\\SAM\\Domains\\Account\\Users");
371 	p = strchr(path, '\\');
372 
373 	do {
374 		if( p != 0)
375 			*p = 0;
376 		else
377 			finished = TRUE;
378 		if((err = RegOpenKeyEx( start, path, 0, WRITE_DAC, &key)) !=
379 						ERROR_SUCCESS) {
380 			fprintf(stderr, "set_sam_tree_access:Failed to open key %s to set \
381 security. Error was %s.\n",
382 					path, str_oserr(err));
383 			return (security_changed ? -2: -1);
384 		}
385 		if((err = RegSetKeySecurity( key, DACL_SECURITY_INFORMATION,
386 									 &sd)) != ERROR_SUCCESS) {
387 			fprintf(stderr, "set_sam_tree_access: Failed to set security on key %s. \
388 Error was %s.\n",
389 					path, str_oserr(err));
390 			RegCloseKey(key);
391 			return (security_changed ? -2: -1);
392 		}
393 		security_changed = TRUE;
394 		RegCloseKey(key);
395 		if(p != 0) {
396 			*p++ = '\\';
397 			p = strchr(p, '\\');
398 		}
399 	} while( !finished );
400 
401 	if(set_userkeys_security( start, path, &sd, &key) != 0)
402 		return -2;
403 	if(return_key == 0)
404 		RegCloseKey(key);
405 	else
406 		*return_key = key;
407 	return 0;
408 }
409 
410 /*
411  * Function to get a little-endian int from an offset into
412  * a byte array.
413  */
414 
get_int(char * array)415 int get_int( char *array )
416 {
417 	return ((array[0]&0xff) + ((array[1]<<8)&0xff00) +
418 		   ((array[2]<<16)&0xff0000) +
419 		   ((array[3]<<24)&0xff000000));
420 }
421 
422 /*
423  * Convert a 7 byte array into an 8 byte des key with odd parity.
424  */
425 
str_to_key(unsigned char * str,unsigned char * key)426 void str_to_key(unsigned char *str,unsigned char *key)
427 {
428 	void des_set_odd_parity(des_cblock *);
429 	int i;
430 
431 	key[0] = str[0]>>1;
432 	key[1] = ((str[0]&0x01)<<6) | (str[1]>>2);
433 	key[2] = ((str[1]&0x03)<<5) | (str[2]>>3);
434 	key[3] = ((str[2]&0x07)<<4) | (str[3]>>4);
435 	key[4] = ((str[3]&0x0F)<<3) | (str[4]>>5);
436 	key[5] = ((str[4]&0x1F)<<2) | (str[5]>>6);
437 	key[6] = ((str[5]&0x3F)<<1) | (str[6]>>7);
438 	key[7] = str[6]&0x7F;
439 	for (i=0;i<8;i++) {
440 		key[i] = (key[i]<<1);
441 	}
442 	des_set_odd_parity((des_cblock *)key);
443 }
444 
445 /*
446  * Function to convert the RID to the first decrypt key.
447  */
448 
sid_to_key1(unsigned long sid,unsigned char deskey[8])449 void sid_to_key1(unsigned long sid,unsigned char deskey[8])
450 {
451 	unsigned char s[7];
452 
453 	s[0] = (unsigned char)(sid & 0xFF);
454 	s[1] = (unsigned char)((sid>>8) & 0xFF);
455 	s[2] = (unsigned char)((sid>>16) & 0xFF);
456 	s[3] = (unsigned char)((sid>>24) & 0xFF);
457 	s[4] = s[0];
458 	s[5] = s[1];
459 	s[6] = s[2];
460 
461 	str_to_key(s,deskey);
462 }
463 
464 /*
465  * Function to convert the RID to the second decrypt key.
466  */
467 
sid_to_key2(unsigned long sid,unsigned char deskey[8])468 void sid_to_key2(unsigned long sid,unsigned char deskey[8])
469 {
470 	unsigned char s[7];
471 
472 	s[0] = (unsigned char)((sid>>24) & 0xFF);
473 	s[1] = (unsigned char)(sid & 0xFF);
474 	s[2] = (unsigned char)((sid>>8) & 0xFF);
475 	s[3] = (unsigned char)((sid>>16) & 0xFF);
476 	s[4] = s[0];
477 	s[5] = s[1];
478 	s[6] = s[2];
479 
480 	str_to_key(s,deskey);
481 }
482 
483 /*
484  * Function to split a 'V' entry into a users name, passwords and comment.
485  */
486 
check_vp(char * vp,int vp_size,char ** username,char ** fullname,char ** comment,char ** homedir,char * lanman,int * got_lanman,char * md4,int * got_md4,DWORD rid)487 int check_vp(char *vp, int vp_size, char **username, char **fullname,
488 			 char **comment, char **homedir,
489 			 char *lanman,int *got_lanman,
490 			 char *md4,  int *got_md4,
491 			 DWORD rid
492 			 )
493 {
494 	des_key_schedule ks1, ks2;
495 	des_cblock deskey1, deskey2;
496 	int username_offset = get_int(vp + 0xC);
497 	int username_len = get_int(vp + 0x10);
498 	int fullname_offset = get_int(vp + 0x18);
499 	int fullname_len = get_int(vp + 0x1c);
500 	int comment_offset = get_int(vp + 0x24);
501 	int comment_len = get_int(vp + 0x28);
502 	int homedir_offset = get_int(vp + 0x48);
503 	int homedir_len = get_int(vp + 0x4c);
504 	int pw_offset = get_int(vp + 0x9c);
505 
506 	*username = 0;
507 	*fullname = 0;
508 	*comment = 0;
509 	*homedir = 0;
510 	*got_lanman = 0;
511 	*got_md4 = 0;
512 
513 	if(username_len < 0 || username_offset < 0 || comment_len < 0 ||
514 			   fullname_len < 0 || homedir_offset < 0 ||
515 		       comment_offset < 0 || pw_offset < 0)
516 		return -1;
517 	username_offset += 0xCC;
518 	fullname_offset += 0xCC;
519 	comment_offset += 0xCC;
520 	homedir_offset += 0xCC;
521 	pw_offset += 0xCC;
522 
523 	if((*username = (char *)malloc(username_len + 1)) == 0) {
524 		fprintf(stderr, "check_vp: malloc fail for username.\n");
525 		return -1;
526 	}
527 	if((*fullname = (char *)malloc(fullname_len + 1)) == 0) {
528 		fprintf(stderr, "check_vp: malloc fail for username.\n");
529 		free(*username);
530 		*username = 0;
531 		return -1;
532 	}
533 	if((*comment = (char *)malloc(comment_len + 1)) == 0) {
534 		fprintf(stderr, "check_vp: malloc fail for comment.\n");
535 		free(*username);
536 		*username = 0;
537 		free(*fullname);
538 		*fullname = 0;
539 		return -1;
540 	}
541 	if((*homedir = (char *)malloc(homedir_len + 1)) == 0) {
542 		fprintf(stderr, "check_vp: malloc fail for homedir.\n");
543 		free(*username);
544 		*username = 0;
545 		free(*fullname);
546 		*fullname = 0;
547 		free(*comment);
548 		*comment = 0;
549 		return -1;
550 	}
551 	wcstombs( *username, (wchar_t *)(vp + username_offset), username_len/sizeof(wchar_t));
552 	(*username)[username_len/sizeof(wchar_t)] = 0;
553 	wcstombs( *fullname, (wchar_t *)(vp + fullname_offset), fullname_len/sizeof(wchar_t));
554 	(*fullname)[fullname_len/sizeof(wchar_t)] = 0;
555 	wcstombs( *comment, (wchar_t *)(vp + comment_offset), comment_len/sizeof(wchar_t));
556 	(*comment)[comment_len/sizeof(wchar_t)] = 0;
557 	wcstombs( *homedir, (wchar_t *)(vp + homedir_offset), homedir_len/sizeof(wchar_t));
558 	(*homedir)[homedir_len/sizeof(wchar_t)] = 0;
559 
560 	if(pw_offset >= vp_size) {
561 		/* No password */
562 		*got_lanman = 0;
563 		*got_md4 = 0;
564 		return 0;
565 	}
566 
567 	/* Check that the password offset plus the size of the
568 	   lanman and md4 hashes fits within the V record. */
569 	if(pw_offset + 32 > vp_size) {
570 		/* Account disabled ? */
571 		*got_lanman = -1;
572 		*got_md4 = -1;
573 		return 0;
574 	}
575 
576 	/* Get the two decrpt keys. */
577 	sid_to_key1(rid,(unsigned char *)deskey1);
578 	des_set_key((des_cblock *)deskey1,ks1);
579 	sid_to_key2(rid,(unsigned char *)deskey2);
580 	des_set_key((des_cblock *)deskey2,ks2);
581 
582 	vp += pw_offset;
583 	/* Decrypt the lanman password hash as two 8 byte blocks. */
584 	des_ecb_encrypt((des_cblock *)vp,
585 					(des_cblock *)lanman, ks1, DES_DECRYPT);
586 	des_ecb_encrypt((des_cblock *)(vp + 8),
587 					(des_cblock *)&lanman[8], ks2, DES_DECRYPT);
588 
589 	vp += 16;
590 	/* Decrypt the NT md4 password hash as two 8 byte blocks. */
591 	des_ecb_encrypt((des_cblock *)vp,
592 					(des_cblock *)md4, ks1, DES_DECRYPT);
593 	des_ecb_encrypt((des_cblock *)(vp + 8),
594 					(des_cblock *)&md4[8], ks2, DES_DECRYPT);
595 
596 	*got_lanman = 1;
597 	*got_md4 = 1;
598 	return 0;
599 }
600 
601 /*
602  * Function to print out a 16 byte array as hex.
603  */
604 
print_hexval(char * val)605 void print_hexval(char *val)
606 {
607 	int i;
608 	for(i = 0; i < 16; i++)
609 		printf("%02X", (unsigned char)val[i]);
610 }
611 
612 /*
613  * Function to strip out any ':' or '\n', '\r' from a text
614  * string.
615  */
616 
strip_text(char * txt)617 void strip_text( char *txt )
618 {
619 	char *p;
620 	for( p = strchr(txt, ':'); p ; p = strchr( p + 1, ':'))
621 		*p = '_';
622 	for( p = strchr(txt, '\n'); p ; p = strchr(p + 1, '\n'))
623 		*p = '_';
624 	for( p = strchr(txt, '\r'); p ; p = strchr(p + 1, '\r'))
625 		*p = '_';
626 }
627 
628 /*
629  * Function to dump a users smbpasswd entry onto stdout.
630  * Returns 0 on success, -1 on fail.
631  */
632 
printout_smb_entry(HKEY user,DWORD rid)633 int printout_smb_entry( HKEY user, DWORD rid )
634 {
635  	DWORD err;
636 	DWORD type;
637 	DWORD size = 0;
638 	char *vp;
639 	char lanman[16];
640 	char md4_hash[16];
641 	char *username;
642 	char *fullname;
643 	char *comment;
644 	char *homedir;
645 	int got_lanman;
646 	int got_md4;
647 
648 	/* Find out how much space we need for the 'V' value. */
649 	if((err = RegQueryValueEx( user, "V", 0, &type, 0, &size))
650 								!= ERROR_SUCCESS) {
651 		fprintf(stderr, "printout_smb_entry: Unable to determine size needed \
652 for user 'V' value. Error was %s.\n.", str_oserr(err));
653 		return -1;
654 	}
655 	if((vp = (char *)malloc(size)) == 0) {
656 		fprintf(stderr, "printout_smb_entry: malloc fail for user entry.\n");
657 		return -1;
658 	}
659 	if((err = RegQueryValueEx( user, "V", 0, &type, (LPBYTE)vp, &size))
660 								!= ERROR_SUCCESS) {
661 		fprintf(stderr, "printout_smb_entry: Unable to read user 'V' value. \
662 Error was %s.\n.", str_oserr(err));
663 		free(vp);
664 		return -1;
665 	}
666 	/* Check heuristics */
667 	if(check_vp(vp, size, &username, &fullname, &comment,
668 						&homedir, lanman, &got_lanman,
669 		               md4_hash, &got_md4, rid) != 0) {
670 		fprintf(stderr, "Failed to parse entry for RID %X\n", rid);
671 		free(vp);
672 		return 0;
673 	}
674 	/* Ensure username of comment don't have any nasty suprises
675 	   for us such as an embedded ':' or '\n' - see multiple UNIX
676 	   passwd field update security bugs for details... */
677 	strip_text( username );
678 	strip_text( fullname );
679 	strip_text( comment );
680 	/* If homedir contains a drive letter this mangles it - but it protects
681 	   the integrity of the smbpasswd file. */
682 	strip_text( homedir );
683 
684 	printf("%s:%d:", username, rid);
685 	if(got_lanman) {
686 		if(got_lanman == -1) /* Disabled account ? */
687 			printf("********************************");
688 		else
689 			print_hexval(lanman);
690 	} else
691 		printf("NO PASSWORD*********************");
692 	printf(":");
693 	if(got_md4) {
694 		if(got_md4 == -1)  /* Disabled account ? */
695 			printf("********************************");
696 		else
697 			print_hexval(md4_hash);
698 	} else
699 		printf("NO PASSWORD*********************");
700 	printf(":");
701 	if(*fullname)
702 		printf("%s", fullname);
703 	if(*fullname && *comment)
704 		printf(",");
705 	if(*comment)
706 		printf("%s", comment);
707 	printf(":");
708 	if(*homedir)
709 		printf("%s", homedir);
710 	printf(":\n");
711 
712 	free(username);
713 	free(comment);
714 	free(homedir);
715 	free(vp);
716 	return 0;
717 }
718 
719 /*
720  * Function to go through all the user SID's - dumping out
721  * their SAM values. Returns 0 on success, -1 on fail.
722  */
723 
enumerate_users(HKEY key)724 int enumerate_users( HKEY key)
725 {
726 	DWORD indx = 0;
727 	DWORD err;
728 	DWORD rid;
729 	char usersid[128];
730 
731 	do {
732 		DWORD size;
733 		FILETIME ft;
734 
735 		size = sizeof(usersid);
736 		err = RegEnumKeyEx(	key, indx, usersid, &size, 0, 0, 0, &ft);
737 		if(err == ERROR_SUCCESS) {
738 			HKEY subkey;
739 
740 			indx++;
741 			if((err = RegOpenKeyEx( key, usersid, 0, KEY_QUERY_VALUE, &subkey)) !=
742 						ERROR_SUCCESS) {
743 				fprintf(stderr, "enumerate_users: Failed to open key %s to read value. \
744 Error was %s.\n",
745 						usersid, str_oserr(err));
746 				RegCloseKey(key);
747 				return -1;
748 			}
749 			rid = strtoul(usersid, 0, 16);
750 			/* Hack as we know there is a Names key here */
751 			if(rid != 0) {
752 				if(printout_smb_entry( subkey, rid ) != 0) {
753 					RegCloseKey(subkey);
754 					return -1;
755 				}
756 			}
757 			RegCloseKey(subkey);
758 		}
759 	} while(err == ERROR_SUCCESS);
760 
761 	if(err != ERROR_NO_MORE_ITEMS) {
762 		RegCloseKey(key);
763 		return -1;
764 	}
765 	return 0;
766 }
767 
768 /*
769  * Print usage message and die.
770  */
usage(const char * arg0)771 void usage(const char *arg0) {
772 	fprintf(stderr, "Usage: %s <\\\\machine>\n", arg0);
773 	exit(-1);
774 }
775 
776 /*
777  * usage: \\machine
778  */
779 
main(int argc,char ** argv)780 int main(int argc, char **argv)
781 {
782 	char username[128];
783 	DWORD size;
784 	HKEY start_key = HKEY_LOCAL_MACHINE;
785 	HKEY users_key;
786 	int err;
787 
788 	if(argc > 2)
789 		usage(argv[0]);
790 
791 	/*
792 	 * Ensure we are running as Administrator before
793 	 * we will run.
794 	 */
795 	size = sizeof(username);
796 	if(GetUserName(username, &size)== FALSE) {
797 		fprintf(stderr, "%s: GetUserName() failed. Error was %s.",
798 			argv[0], str_oserr(GetLastError()));
799 		return -1;
800 	}
801 
802 
803 	/*
804 	 * Open a connection to the remote machines registry.
805 	 */
806 	if(argc == 2) {
807 		if((err = RegConnectRegistry( argv[1], HKEY_LOCAL_MACHINE, &start_key)) !=
808 			ERROR_SUCCESS) {
809 			fprintf(stderr, "%s: Failed to connect to registry on remote computer %s.\
810 Error was %s.\n", argv[0], argv[1], str_oserr(err));
811 			return -1;
812 		}
813 	}
814 
815 	/*
816 	 * We need to get to HKEY_LOCAL_MACHINE\SECURITY\SAM\Domains\Account\Users.
817 	 * The security on this key normally doesn't allow Administrators
818 	 * to read - we need to add this.
819 	 */
820 
821 	if((err = set_sam_tree_access( start_key, &users_key)) != 0) {
822 		if(err == -2)
823 			restore_sam_tree_access( start_key);
824 		return -1;
825 	}
826 	/* Print the users SAM entries in smbpasswd format onto stdout. */
827 	enumerate_users( users_key );
828 	RegCloseKey(users_key);
829 	/* reset the security on the SAM */
830 	restore_sam_tree_access( start_key );
831 	if(start_key != HKEY_LOCAL_MACHINE)
832 		RegCloseKey(start_key);
833 	return 0;
834 }