1 /*
2  *               Windows to Linux user mapping for ntfs-3g
3  *
4  *
5  * Copyright (c) 2007-2016 Jean-Pierre Andre
6  *
7  *    A quick'n dirty program scanning owners of files in
8  *      "c:\Documents and Settings" (and "c:\Users")
9  *      and asking user to map them to Linux accounts
10  *
11  *          History
12  *
13  *  Sep 2007
14  *     - first version, limited to Win32
15  *
16  *  Oct 2007
17  *     - ported to Linux (rewritten would be more correct)
18  *
19  *  Nov 2007 Version 1.0.0
20  *     - added more defaults
21  *
22  *  Nov 2007 Version 1.0.1
23  *     - avoided examining files whose name begin with a '$'
24  *
25  *  Jan 2008 Version 1.0.2
26  *     - moved user mapping file to directory .NTFS-3G (hidden for Linux)
27  *     - fixed an error case in Windows version
28  *
29  *  Nov 2008 Version 1.1.0
30  *     - fixed recursions for account in Linux version
31  *     - searched owner in c:\Users (standard location for Vista)
32  *
33  *  May 2009 Version 1.1.1
34  *     - reordered mapping records to limit usage of same SID for user and group
35  *     - fixed decoding SIDs on 64-bit systems
36  *     - fixed a pointer to dynamic data in mapping tables
37  *     - fixed default mapping on Windows
38  *     - fixed bug for renaming UserMapping on Windows
39  *
40  *  May 2009 Version 1.1.2
41  *     - avoided selecting DOS names on Linux
42  *
43  *  Nov 2009 Version 1.1.3
44  *     - silenced compiler warnings for unused parameters
45  *
46  *  Jan 2010 Version 1.1.4
47  *     - fixed compilation problems for Mac OSX (Erik Larsson)
48  *
49  *  Apr 2014 Version 1.1.5
50  *     - displayed the parent directory of selected files
51  *
52  *  May 2014 Version 1.1.6
53  *     - fixed a wrong function header
54  *
55  *  Mar 2016 Version 1.2.0
56  *     - reorganized to rely on libntfs-3g even on Windows
57  */
58 
59 /*
60  * This program is free software; you can redistribute it and/or modify
61  * it under the terms of the GNU General Public License as published by
62  * the Free Software Foundation; either version 2 of the License, or
63  * (at your option) any later version.
64  *
65  * This program is distributed in the hope that it will be useful,
66  * but WITHOUT ANY WARRANTY; without even the implied warranty of
67  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
68  * GNU General Public License for more details.
69  *
70  * You should have received a copy of the GNU General Public License
71  * along with this program (in the main directory of the NTFS-3G
72  * distribution in the file COPYING); if not, write to the Free Software
73  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
74  */
75 
76 /*
77  *		General parameters which may have to be adapted to needs
78  */
79 
80 #define USERMAPVERSION "1.2.0"
81 #define MAPDIR ".NTFS-3G"
82 #define MAPFILE "UserMapping"
83 #define MAXATTRSZ 2048
84 #define MAXSIDSZ 80
85 #define MAXNAMESZ 256
86 #define OWNERS1 "Documents and Settings"
87 #define OWNERS2 "Users"
88 
89 #include "config.h"
90 
91 #ifdef HAVE_STDIO_H
92 #include <stdio.h>
93 #endif
94 #ifdef HAVE_STDLIB_H
95 #include <stdlib.h>
96 #endif
97 #ifdef HAVE_STRING_H
98 #include <string.h>
99 #endif
100 #ifdef HAVE_UNISTD_H
101 #include <unistd.h>
102 #endif
103 #ifdef HAVE_TIME_H
104 #include <time.h>
105 #endif
106 #ifdef HAVE_GETOPT_H
107 #include <getopt.h>
108 #endif
109 #ifdef HAVE_ERRNO_H
110 #include <errno.h>
111 #endif
112 #ifdef HAVE_FCNTL_H
113 #include <fcntl.h>
114 #endif
115 #ifdef HAVE_SYS_TYPES_H
116 #include <sys/types.h>
117 #endif
118 #ifdef HAVE_SYS_STAT_H
119 #include <sys/stat.h>
120 #endif
121 
122 #include "types.h"
123 #include "endians.h"
124 #include "support.h"
125 #include "layout.h"
126 #include "param.h"
127 #include "ntfstime.h"
128 #include "device_io.h"
129 #include "device.h"
130 #include "logging.h"
131 #include "runlist.h"
132 #include "mft.h"
133 #include "inode.h"
134 #include "attrib.h"
135 #include "bitmap.h"
136 #include "index.h"
137 #include "volume.h"
138 #include "unistr.h"
139 #include "mst.h"
140 #include "security.h"
141 #include "utils.h"
142 #include "misc.h"
143 
144 #ifdef HAVE_WINDOWS_H
145 /*
146  *	Including <windows.h> leads to numerous conflicts with layout.h
147  *	so define a few needed Windows calls unrelated to ntfs-3g
148  */
149 BOOL WINAPI LookupAccountNameA(const char*, const char*, void*,
150 		u32*, char*, u32*, s32*);
151 BOOL WINAPI GetUserNameA(char*, u32*);
152 #endif
153 
154 #ifdef HAVE_WINDOWS_H
155 #define DIRSEP "\\"
156 #else
157 #define DIRSEP "/"
158 #endif
159 
160 #ifdef HAVE_WINDOWS_H
161 #define BANNER "Generated by ntfsusermap for Windows, v " USERMAPVERSION
162 #else
163 #define BANNER "Generated by ntfsusermap for Linux, v " USERMAPVERSION
164 #endif
165 
166 typedef enum { DENIED, AGREED } boolean;
167 
168 enum STATES { STATE_USERS, STATE_HOMES, STATE_BASE } ;
169 
170 struct CALLBACK {
171 	const char *accname;
172 	const char *dir;
173 	int levels;
174 	enum STATES docset;
175 } ;
176 
177 typedef int (*dircallback)(struct CALLBACK *context, char *ntfsname,
178 	int length, int type, long long pos, unsigned long long mft_ref,
179 	unsigned int dt_type);
180 
181 
182 struct USERMAPPING {
183 	struct USERMAPPING *next;
184 	const char *uidstr;
185 	const char *gidstr;
186 	const char *sidstr;
187 	const unsigned char *sid;
188 	const char *login;
189 	boolean defined;
190 };
191 
192 struct USERMAPPING *firstmapping;
193 struct USERMAPPING *lastmapping;
194 
195 #ifdef HAVE_WINDOWS_H
196 char *currentwinname;
197 char *currentdomain;
198 unsigned char *currentsid;
199 #endif
200 
201 void *ntfs_handle;
202 void *ntfs_context = (void*)NULL;
203 
204 /*
205  *		Open and close a volume in read-only mode
206  *	assuming a single volume needs to be opened at any time
207  */
208 
open_volume(const char * volume)209 static boolean open_volume(const char *volume)
210 {
211 	boolean ok;
212 
213 	ok = DENIED;
214 	if (!ntfs_context) {
215 		ntfs_context = ntfs_initialize_file_security(volume,
216 						NTFS_MNT_RDONLY);
217 		if (ntfs_context) {
218 			fprintf(stderr,"\"%s\" opened\n",volume);
219 			ok = AGREED;
220 		} else {
221 			fprintf(stderr,"Could not open \"%s\"\n",volume);
222 #ifdef HAVE_WINDOWS_H
223 			if (errno == EACCES)
224 				fprintf(stderr,"Make sure you have"
225 					" Administrator rights\n");
226 #else
227 			fprintf(stderr,"Make sure \"%s\" is not mounted\n",
228 				volume);
229 #endif
230 		}
231 	} else
232 		fprintf(stderr,"A volume is already open\n");
233 	return (ok);
234 }
235 
close_volume(const char * volume)236 static boolean close_volume(const char *volume)
237 {
238 	boolean r;
239 
240 	r = ntfs_leave_file_security(ntfs_context) ? AGREED : DENIED;
241 	if (r)
242 		fprintf(stderr,"\"%s\" closed\n",volume);
243 	else
244 		fprintf(stderr,"Could not close \"%s\"\n",volume);
245 	ntfs_context = (void*)NULL;
246 	return (r);
247 }
248 
249 /*
250  *		A poor man's conversion of Unicode to UTF8
251  *	We are assuming outputs to terminal expect UTF8
252  */
253 
to_utf8(char * dst,const char * src,unsigned int cnt)254 static void to_utf8(char *dst, const char *src, unsigned int cnt)
255 {
256 	unsigned int ch;
257 	unsigned int i;
258 
259 	for (i=0; i<cnt; i++) {
260 		ch = *src++ & 255;
261 		ch += (*src++ & 255) << 8;
262 		if (ch < 0x80)
263 			*dst++ = ch;
264 		else
265 			if (ch < 0x1000) {
266 				*dst++ = 0xc0 + (ch >> 6);
267 				*dst++ = 0x80 + (ch & 63);
268 			} else {
269 				*dst++ = 0xe0 + (ch >> 12);
270 				*dst++ = 0x80 + ((ch >> 6) & 63);
271 				*dst++ = 0x80 + (ch & 63);
272 			}
273 	}
274 	*dst = 0;
275 }
276 
utf8_size(const char * src,unsigned int cnt)277 static int utf8_size(const char *src, unsigned int cnt)
278 {
279 	unsigned int ch;
280 	unsigned int i;
281 	int size;
282 
283 	size = 0;
284 	for (i=0; i<cnt; i++) {
285 		ch = *src++ & 255;
286 		ch += (*src++ & 255) << 8;
287 		if (ch < 0x80)
288 			size++;
289 		else
290 			if (ch < 0x1000)
291 				size += 2;
292 			else
293 				size += 3;
294 	}
295 	return (size);
296 }
297 
welcome(void)298 static void welcome(void)
299 {
300 	printf("\nThis tool will help you to build a mapping of"
301 			" Windows users\n");
302 	printf("to Linux users.\n");
303 	printf("Be prepared to give Linux user id (uid) and group id (gid)\n");
304 	printf("for owners of files which will be selected.\n");
305 }
306 
get2l(const unsigned char * attr,int p)307 static unsigned int get2l(const unsigned char *attr, int p)
308 {
309 	int i;
310 	unsigned int v;
311 
312 	v = 0;
313 	for (i = 0; i < 2; i++)
314 		v += (attr[p + i] & 255) << (8 * i);
315 	return (v);
316 }
317 
get4l(const unsigned char * attr,int p)318 static unsigned long get4l(const unsigned char *attr, int p)
319 {
320 	int i;
321 	unsigned long v;
322 
323 	v = 0;
324 	for (i = 0; i < 4; i++)
325 		v += (attr[p + i] & 255L) << (8 * i);
326 	return (v);
327 }
328 
get6h(const unsigned char * attr,int p)329 static unsigned long long get6h(const unsigned char *attr, int p)
330 {
331 	int i;
332 	unsigned long long v;
333 
334 	v = 0;
335 	for (i = 0; i < 6; i++)
336 		v = (v << 8) + (attr[p + i] & 255L);
337 	return (v);
338 }
339 
decodesid(const unsigned char * sid)340 static char *decodesid(const unsigned char *sid)
341 {
342 	char *str;
343 	int i;
344 	unsigned long long auth;
345 	unsigned long subauth;
346 
347 	str = (char *)malloc(MAXSIDSZ);
348 	if (str) {
349 		strcpy(str, "S");
350 							/* revision */
351 		sprintf(&str[strlen(str)], "-%d", sid[0]);
352 							/* main authority */
353 		auth = get6h(sid, 2);
354 #ifdef HAVE_WINDOWS_H
355 		sprintf(&str[strlen(str)], "-%I64u", auth);
356 #else
357 		sprintf(&str[strlen(str)], "-%llu", auth);
358 #endif
359 		for (i = 0; (i < 8) && (i < sid[1]); i++) {
360 							/* sub-authority */
361 			subauth = get4l(sid, 8 + 4 * i);
362 			sprintf(&str[strlen(str)], "-%lu", subauth);
363 		}
364 	}
365 	return (str);
366 }
367 
368 /*
369  *        Test whether a generic group (S-1-5-21- ... -513)
370  */
371 
isgenericgroup(const char * sid)372 static boolean isgenericgroup(const char *sid)
373 {
374 	boolean yes;
375 
376 	yes = !strncmp(sid,"S-1-5-21-",9)
377 		&& !strcmp(strrchr(sid,'-'),"-513");
378 	return (yes);
379 }
380 
makegroupsid(const unsigned char * sid)381 static unsigned char *makegroupsid(const unsigned char *sid)
382 {
383 	static unsigned char groupsid[MAXSIDSZ];
384 	int size;
385 
386 	size = 8 + 4*sid[1];
387 	memcpy(groupsid, sid, size);
388 		/* replace last level by 513 */
389 	groupsid[size - 4] = 1;
390 	groupsid[size - 3] = 2;
391 	groupsid[size - 2] = 0;
392 	groupsid[size - 1] = 0;
393 	return (groupsid);
394 }
395 
askmapping(const char * accname,const char * filename,const char * dir,const unsigned char * sid,int type,struct USERMAPPING * mapping,char * sidstr)396 static void askmapping(const char *accname, const char *filename,
397 		const char *dir, const unsigned char *sid, int type,
398 		struct USERMAPPING *mapping, char *sidstr)
399 {
400 	char buf[81];
401 	char *idstr;
402 	char *login;
403 	int sidsz;
404 	boolean reject;
405 	char *p;
406 
407 	do {
408 		reject = DENIED;
409 		printf("\n");
410 		if (accname)
411 			printf("Under Windows login \"%s\"\n", accname);
412 		if (dir) {
413 #ifdef HAVE_WINDOWS_H
414 			char *wdir;
415 
416 			wdir = strdup(dir);
417 			if (wdir) {
418 				for (p=wdir; *p; p++)
419 					if (*p == '/')
420 						*p = '\\';
421 				printf("   in directory \"%s\"\n",wdir);
422 				free(wdir);
423 			}
424 #else
425 			printf("   in directory \"%s\"\n",dir);
426 #endif
427 		}
428 		printf("   file \"%s\" has no mapped %s\n",
429 			       filename,(type ? "group" : "owner"));
430 		printf("By which Linux login should this file be owned ?\n");
431 		printf("Enter %s of login, or just press \"enter\" if"
432 			" this file\n", (type ? "gid" : "uid"));
433 		printf("does not belong to a user, or you do not know"
434 			" to whom\n");
435 		printf("\n");
436 		if (type)
437 			printf("Group : ");
438 		else
439 			printf("User : ");
440 		p = fgets(buf, 80, stdin);
441 		if (p && p[0] && (p[strlen(p) - 1] == '\n'))
442 			p[strlen(p) - 1] = '\0';
443 
444 		if (p && p[0]
445 			 && ((p[0] == '0') || !strcmp(p, "root"))) {
446 			printf("Please do not map users to root\n");
447 			printf("Administrators will be mapped automatically\n");
448 			reject = AGREED;
449 		}
450 		if (reject)
451 			printf("Please retry\n");
452 	} while (reject);
453 	if (!mapping) {
454 		mapping =
455 		    (struct USERMAPPING *)
456 		    malloc(sizeof(struct USERMAPPING));
457 		mapping->next = (struct USERMAPPING *)NULL;
458 		mapping->defined = DENIED;
459 		if (lastmapping)
460 			lastmapping->next = mapping;
461 		else
462 			firstmapping = mapping;
463 		lastmapping = mapping;
464 	}
465 	if (mapping) {
466 		if (p && p[0]) {
467 			idstr = (char *)malloc(strlen(p) + 1);
468 			if (idstr) {
469 				strcpy(idstr, p);
470 				if (type) {
471 					mapping->uidstr = "";
472 					mapping->gidstr = idstr;
473 				} else {
474 					mapping->uidstr = idstr;
475 					mapping->gidstr = idstr;
476 				}
477 				mapping->defined = AGREED;
478 			}
479 		}
480 		mapping->sidstr = sidstr;
481 		if (accname) {
482 			login = (char*)malloc(strlen(accname) + 1);
483 			if (login)
484 				strcpy(login,accname);
485 			mapping->login = login;
486 		} else
487 			mapping->login = (char*)NULL;
488 		sidsz = 8 + sid[1]*4;
489 		p = (char*)malloc(sidsz);
490 		if (p) {
491 			memcpy(p, sid, sidsz);
492 		}
493 		mapping->sid = (unsigned char*)p;
494 	}
495 }
496 
domapping(const char * accname,const char * filename,const char * dir,const unsigned char * sid,int type)497 static void domapping(const char *accname, const char *filename,
498 		const char *dir, const unsigned char *sid, int type)
499 {
500 	char *sidstr;
501 	struct USERMAPPING *mapping;
502 
503 	if ((get6h(sid, 2) == 5) && (get4l(sid, 8) == 21)) {
504 		sidstr = decodesid(sid);
505 		mapping = firstmapping;
506 		while (mapping && strcmp(mapping->sidstr, sidstr))
507 			mapping = mapping->next;
508 		if (mapping
509 		    && (mapping->defined
510 			 || !accname
511 			 || !strcmp(mapping->login, accname)))
512 			free(sidstr);	/* decision already known */
513 		else {
514 			askmapping(accname, filename, dir, sid, type,
515 					mapping, sidstr);
516 		}
517 	}
518 }
519 
listaclusers(const char * accname,const unsigned char * attr,int off)520 static void listaclusers(const char *accname, const unsigned char *attr,
521 				int off)
522 {
523 	int i;
524 	int cnt;
525 	int x;
526 
527 	cnt = get2l(attr, off + 4);
528 	x = 8;
529 	for (i = 0; i < cnt; i++) {
530 		domapping(accname, (char *)NULL, (char*)NULL,
531                                        &attr[off + x + 8], 2);
532 		x += get2l(attr, off + x + 2);
533 	}
534 }
535 
account(const char * accname,const char * dir,const char * name,int type)536 static void account(const char *accname, const char *dir,
537 			const char *name, int type)
538 {
539 	unsigned char attr[MAXATTRSZ];
540 	u32 attrsz;
541 	char *fullname;
542 
543 	fullname = (char *)malloc(strlen(dir) + strlen(name) + 2);
544 	if (fullname) {
545 		strcpy(fullname, dir);
546 		strcat(fullname, "/");
547 		strcat(fullname, name);
548 		if (ntfs_get_file_security(ntfs_context,
549 				fullname, OWNER_SECURITY_INFORMATION,
550 				(char*)attr, MAXATTRSZ, &attrsz)) {
551 			domapping(accname, name, dir, &attr[20], 0);
552 			attrsz = 0;
553 			if (ntfs_get_file_security(ntfs_context,
554 			     fullname, GROUP_SECURITY_INFORMATION,
555 			     (char*)attr, MAXATTRSZ, &attrsz))
556 				domapping(accname, name, dir, &attr[20], 1);
557 			else
558 				printf("   No group SID\n");
559 			attrsz = 0;
560 			if (ntfs_get_file_security(ntfs_context,
561 			     fullname, DACL_SECURITY_INFORMATION,
562 			     (char*)attr, MAXATTRSZ, &attrsz)) {
563 				if (type == 0)
564 					listaclusers(accname, attr, 20);
565 			} else
566 				printf("   No discretionary access control"
567 					" list for %s !\n", dir);
568 		}
569 	free(fullname);
570 	}
571 }
572 
573 /*
574  *		recursive search of file owners and groups in a directory
575  */
576 
577 static boolean recurse(const char *accname, const char *dir,
578 			int levels, enum STATES docset);
579 
callback(void * ctx,const ntfschar * ntfsname,const int length,const int type,const s64 pos,const MFT_REF mft_ref,const unsigned int dt_type)580 static int callback(void *ctx, const ntfschar *ntfsname,
581 		const int length, const int type,
582 		const s64 pos __attribute__((unused)),
583 		const MFT_REF mft_ref __attribute__((unused)),
584 		const unsigned int dt_type __attribute__((unused)))
585 {
586 	struct CALLBACK *context;
587 	char *fullname;
588 	char *accname;
589 	char *name;
590 
591 	context = (struct CALLBACK*)ctx;
592 	fullname = (char *)malloc(strlen(context->dir)
593 			 + utf8_size((const char*)ntfsname, length) + 2);
594 	if (fullname) {
595 			/* No "\\" when interfacing libntfs-3g */
596 		if (strcmp(context->dir,"/")) {
597 			strcpy(fullname, context->dir);
598 			strcat(fullname, "/");
599 		} else
600 			strcpy(fullname,"/");
601 			/* Unicode to ascii conversion by a lazy man */
602 		name = &fullname[strlen(fullname)];
603 		to_utf8(name, (const char*)ntfsname, length);
604 			/* ignore special files and DOS names */
605 		if ((type != 2)
606 		   && strcmp(name,".")
607 		   && strcmp(name,"..")
608 		   && (name[0] != '$')) {
609 			switch (context->docset) {
610 			case STATE_USERS :
611 					/*
612 					 * only "Documents and Settings"
613 					 * or "Users"
614 					 */
615 				if (!strcmp(name,OWNERS1)
616 				   || !strcmp(name,OWNERS2)) {
617 					recurse((char*)NULL, fullname, 2,
618 							STATE_HOMES);
619 				}
620 				break;
621 					/*
622 					 * within "Documents and Settings"
623 					 * or "Users"
624 					 */
625 			case STATE_HOMES :
626 				accname = (char*)malloc(strlen(name) + 1);
627 				if (accname) {
628 					strcpy(accname, name);
629 					if (context->levels > 0)
630 						recurse(name, fullname,
631 							context->levels - 1,
632 							STATE_BASE);
633 				}
634 				break;
635 					/*
636 					 * not related to "Documents and
637 					 * Settings" or "Users"
638 					 */
639 			case STATE_BASE :
640 				account(context->accname, context->dir,
641 					name, 1);
642 				if (context->levels > 0)
643 					recurse(context->accname, fullname,
644 						context->levels - 1,
645 						STATE_BASE);
646 				break;
647 			}
648 		}
649 		free(fullname);
650 	}
651 /* check expected return value */
652 	return (0);
653 }
654 
recurse(const char * accname,const char * dir,int levels,enum STATES docset)655 static boolean recurse(const char *accname, const char *dir,
656 			int levels, enum STATES docset)
657 {
658 	struct CALLBACK context;
659 	boolean err;
660 
661 	err = DENIED;
662 	context.dir = dir;
663 	context.accname = accname;
664 	context.levels = levels;
665 	context.docset = docset;
666 	ntfs_read_directory(ntfs_context,dir,callback,&context);
667 	return (!err);
668 }
669 
670 /*
671  *          Search directory "Documents and Settings" for user accounts
672  */
673 
getusers(const char * dir,int levels)674 static boolean getusers(const char *dir, int levels)
675 {
676 	boolean err;
677 	struct CALLBACK context;
678 
679 	printf("* Search for \"" OWNERS1 "\" and \"" OWNERS2 "\"\n");
680 	err = DENIED;
681 	context.dir = dir;
682 	context.accname = (const char*)NULL;
683 	context.levels = levels;
684 	context.docset = STATE_USERS;
685 	ntfs_read_directory(ntfs_context,dir,callback,&context);
686 	printf("* Search for other directories %s\n",dir);
687 	context.docset = STATE_BASE;
688 	ntfs_read_directory(ntfs_context,dir,callback,&context);
689 	return (!err);
690 }
691 
692 #ifdef HAVE_WINDOWS_H
693 /*
694  *		Get the current login name (Win32 only)
695  */
696 
loginname(boolean silent)697 static void loginname(boolean silent)
698 {
699 	char *winname;
700 	char *domain;
701 	unsigned char *sid;
702 	u32 namesz;
703 	u32 sidsz;
704 	u32 domainsz;
705 	s32 nametype;
706 	boolean ok;
707 	int r;
708 
709 	ok = FALSE;
710 	winname = (char*)malloc(MAXNAMESZ);
711 	domain = (char*)malloc(MAXNAMESZ);
712 	sid = (char*)malloc(MAXSIDSZ);
713 
714 	namesz = MAXNAMESZ;
715 	domainsz = MAXNAMESZ;
716 	sidsz = MAXSIDSZ;
717 	if (winname
718 	    && domain
719 	    && sid
720 	    && GetUserNameA(winname,&namesz)) {
721 		winname[namesz] = '\0';
722 		if (!silent)
723 			printf("Your current user name is %s\n",winname);
724 		nametype = 1;
725 		r = LookupAccountNameA((char*)NULL,winname,sid,&sidsz,
726 			domain,&domainsz,&nametype);
727 		if (r) {
728 			domain[domainsz] = '\0';
729 			if (!silent)
730 				printf("Your account domain is %s\n",domain);
731 			ok = AGREED;
732 		}
733 	   }
734 	if (ok) {
735 		currentwinname = winname;
736 		currentdomain = domain;
737 		currentsid = sid;
738 	} else {
739 		currentwinname = (char*)NULL;
740 		currentdomain = (char*)NULL;
741 		currentsid = (unsigned char*)NULL;
742 	}
743 }
744 
745 /*
746  *		Minimal output on stdout
747  */
748 
minimal(unsigned char * sid)749 static boolean minimal(unsigned char *sid)
750 {
751 	const unsigned char *groupsid;
752 	boolean ok;
753 
754 	ok = DENIED;
755 	if (sid) {
756 		groupsid = makegroupsid(sid);
757 		printf("# %s\n",BANNER);
758 		printf("# For Windows account \"%s\" in domain \"%s\"\n",
759 			currentwinname, currentdomain);
760 		printf("# Replace \"user\" and \"group\" hereafter by"
761 			" matching Linux login\n");
762 		printf("user::%s\n",decodesid(sid));
763 		printf(":group:%s\n",decodesid(groupsid));
764 		ok = AGREED;
765 	}
766 	return (ok);
767 }
768 
769 #endif
770 
771 /*
772  *		Create a user mapping file
773  *
774  *	From now on, partitions which were opened through ntfs-3g
775  *	are closed, and we use the system drivers to create the file.
776  *	On Windows, we can write on a partition which was analyzed.
777  */
778 
outputmap(const char * volume,const char * dir)779 static boolean outputmap(const char *volume, const char *dir)
780 {
781 	char buf[256];
782 	int fn;
783 	char *fullname;
784 	char *backup;
785 	struct USERMAPPING *mapping;
786 	boolean done;
787 	boolean err;
788 	boolean undecided;
789 	struct stat st;
790 	int s;
791 
792 	done = DENIED;
793 	fullname = (char *)malloc(strlen(MAPFILE) + 1
794 				+ strlen(volume) + 1
795 				+ (dir ? strlen(dir) + 1 : 0));
796 	if (fullname) {
797 #ifdef HAVE_WINDOWS_H
798 		strcpy(fullname, volume);
799 		if (dir && dir[0]) {
800 			strcat(fullname, DIRSEP);
801 			strcat(fullname,dir);
802 		}
803 
804 			/* build directory, if not present */
805 		if (stat(fullname,&st) && (errno == ENOENT)) {
806 			printf("* Creating directory %s\n", fullname);
807 			mkdir(fullname);
808 		}
809 
810 		strcat(fullname, DIRSEP);
811 		strcat(fullname, MAPFILE);
812 		printf("\n");
813 #else
814 		strcpy(fullname, MAPFILE);
815 		printf("\n");
816 #endif
817 
818 		s = stat(fullname,&st);
819 		if (!s) {
820 			backup = (char*)malloc(strlen(fullname + 5));
821 			strcpy(backup,fullname);
822 			strcat(backup,".bak");
823 #ifdef HAVE_WINDOWS_H
824 			unlink(backup);
825 #endif
826 			if (rename(fullname,backup))
827 				printf("* Old mapping file moved to %s\n",
828 					backup);
829 		}
830 
831 		printf("* Creating file %s\n", fullname);
832 		err = DENIED;
833 #ifdef HAVE_WINDOWS_H
834 		fn = open(fullname,O_CREAT + O_TRUNC + O_WRONLY + O_BINARY,
835 			S_IREAD + S_IWRITE);
836 #else
837 		fn = open(fullname,O_CREAT + O_TRUNC + O_WRONLY,
838 			S_IREAD + S_IWRITE);
839 #endif
840 		if (fn > 0) {
841 			sprintf(buf,"# %s\n",BANNER);
842 			if (!write(fn,buf,strlen(buf)))
843 				err = AGREED;
844 			printf("%s",buf);
845 			undecided = DENIED;
846 				/* records for owner only or group only */
847 			for (mapping = firstmapping; mapping && !err;
848 			     mapping = mapping->next)
849 				if (mapping->defined
850 				    && (!mapping->uidstr[0]
851 					    || !mapping->gidstr[0])) {
852 					sprintf(buf,"%s:%s:%s\n",
853 						mapping->uidstr,
854 						mapping->gidstr,
855 						mapping->sidstr);
856 					if (!write(fn,buf,strlen(buf)))
857 						err = AGREED;
858 					printf("%s",buf);
859 				} else
860 					undecided = AGREED;
861 				/* records for both owner and group */
862 			for (mapping = firstmapping; mapping && !err;
863 			     mapping = mapping->next)
864 				if (mapping->defined
865 				    && mapping->uidstr[0]
866 				    && mapping->gidstr[0]) {
867 					sprintf(buf,"%s:%s:%s\n",
868 						mapping->uidstr,
869 						mapping->gidstr,
870 						mapping->sidstr);
871 					if (!write(fn,buf,strlen(buf)))
872 						err = AGREED;
873 					printf("%s",buf);
874 				} else
875 					undecided = AGREED;
876 			done = !err;
877 			close(fn);
878 			if (undecided) {
879 				printf("Undecided :\n");
880 				for (mapping = firstmapping; mapping;
881 				     mapping = mapping->next)
882 					if (!mapping->defined) {
883 						printf("   %s\n",
884 						mapping->sidstr);
885 					}
886 			}
887 #ifndef HAVE_WINDOWS_H
888 			printf("\n* You will have to move the file \""
889 					MAPFILE "\"\n");
890 			printf("  to directory \"" MAPDIR "\" after"
891 					" mounting\n");
892 #endif
893 		}
894 	}
895 	if (!done)
896 		fprintf(stderr, "* Could not create mapping file \"%s\"\n",
897 				fullname);
898 	return (done);
899 }
900 
sanitize(void)901 static boolean sanitize(void)
902 {
903 	char buf[81];
904 	boolean ok;
905 	int ownercnt;
906 	int groupcnt;
907 	struct USERMAPPING *mapping;
908 	struct USERMAPPING *firstowner;
909 	struct USERMAPPING *genericgroup;
910 	struct USERMAPPING *group;
911 	char *sidstr;
912 
913 		/* count owners and groups */
914 		/* and find first user, and a generic group */
915 	ownercnt = 0;
916 	groupcnt = 0;
917 	firstowner = (struct USERMAPPING*)NULL;
918 	genericgroup = (struct USERMAPPING*)NULL;
919 	for (mapping=firstmapping; mapping; mapping=mapping->next) {
920 		if (mapping->defined && mapping->uidstr[0]) {
921 			if (!ownercnt)
922 				firstowner = mapping;
923 			ownercnt++;
924 		}
925 		if (mapping->defined && mapping->gidstr[0]
926 					&& !mapping->uidstr[0]) {
927 			groupcnt++;
928 		}
929 		if (!mapping->defined && isgenericgroup(mapping->sidstr)) {
930 			genericgroup = mapping;
931 		}
932 	}
933 #ifdef HAVE_WINDOWS_H
934 		/* no user defined, on Windows, suggest a mapping */
935 		/* based on account currently used */
936 	if (!ownercnt && currentwinname && currentsid) {
937 		char *owner;
938 		char *p;
939 
940 		printf("\nYou have defined no file owner,\n");
941 		printf("   please enter the Linux login which should"
942 			" be mapped\n");
943 		printf("   to account you are currently using\n");
944 		printf("   Linux user ? ");
945 		p = fgets(buf, 80, stdin);
946 		if (p && p[0] && (p[strlen(p) - 1] == '\n'))
947 			p[strlen(p) - 1] = '\0';
948 		if (p && p[0]) {
949 			firstowner = (struct USERMAPPING*)malloc(
950 						sizeof(struct USERMAPPING));
951 			owner = (char*)malloc(strlen(p) + 1);
952 			if (firstowner && owner) {
953 				strcpy(owner, p);
954 				firstowner->next = firstmapping;
955 				firstowner->uidstr = owner;
956 				firstowner->gidstr = "";
957 				firstowner->sidstr = decodesid(currentsid);
958 				firstowner->sid = currentsid;
959 				firstmapping = firstowner;
960 				ownercnt++;
961 				/* prefer a generic group with the same
962 				 * authorities */
963 				for (mapping=firstmapping; mapping;
964 						mapping=mapping->next)
965 					if (!mapping->defined
966 					    && isgenericgroup(mapping->sidstr)
967 					    && !memcmp(firstowner->sidstr,
968 							mapping->sidstr,
969 							strlen(mapping
970 								->sidstr)-3))
971 						genericgroup = mapping;
972 			}
973 		}
974 	}
975 #endif
976 	if (ownercnt) {
977 			/*
978 			 *   No group was selected, but there were a generic
979 			 *   group, insist in using it, associated to the
980 			 *   first user
981 			 */
982 		if (!groupcnt) {
983 			printf("\nYou have defined no group,"
984 					" this can cause problems\n");
985 			printf("Do you accept defining a standard group ?\n");
986 			if (!fgets(buf,80,stdin)
987 			   || ((buf[0] != 'n')
988 			      && (buf[0] != 'N'))) {
989 				if (genericgroup) {
990 					genericgroup->uidstr = "";
991 					genericgroup->gidstr =
992 							firstowner->uidstr;
993 					genericgroup->defined = AGREED;
994 				} else {
995 					group = (struct USERMAPPING*)
996 						malloc(sizeof(
997 							struct USERMAPPING));
998 					sidstr = decodesid(
999 						makegroupsid(firstowner->sid));
1000 					if (group && sidstr) {
1001 						group->uidstr = "";
1002 						group->gidstr = firstowner->
1003 								uidstr;
1004 						group->sidstr = sidstr;
1005 						group->defined = AGREED;
1006 						group->next = firstmapping;
1007 						firstmapping = group;
1008 					}
1009 				}
1010 			}
1011 		}
1012 		ok = AGREED;
1013 	} else {
1014 		printf("\nYou have defined no user, no mapping can be built\n");
1015 		ok = DENIED;
1016 	}
1017 
1018 	return (ok);
1019 }
1020 
checkoptions(int argc,char * argv[],boolean silent)1021 static boolean checkoptions(int argc, char *argv[] __attribute__((unused)),
1022 				boolean silent __attribute__((unused)))
1023 {
1024 	boolean err;
1025 #ifdef HAVE_WINDOWS_H
1026 	int xarg;
1027 	const char *pvol;
1028 
1029 	if (silent) {
1030 		err = (argc != 1);
1031 	} else {
1032 		err = (argc < 2);
1033 		for (xarg=1; (xarg<argc) && !err; xarg++) {
1034 			pvol = argv[xarg];
1035 			if (pvol[0] && (pvol[1] == ':') && !pvol[2]) {
1036 				err = !(((pvol[0] >= 'A') && (pvol[0] <= 'Z'))
1037 					|| ((pvol[0] >= 'a')
1038 						&& (pvol[0] <= 'z')));
1039 			}
1040 		}
1041 	}
1042 	if (err) {
1043 		fprintf(stderr, "Usage : ntfsusermap [vol1: [vol2: ...]]\n");
1044 		fprintf(stderr, "    \"voln\" are the letters of the partition"
1045 				" to share with Linux\n");
1046 		fprintf(stderr, "        eg C:\n");
1047 		fprintf(stderr, "    the Windows system partition should be"
1048 				" named first\n");
1049 		if (silent) {
1050 			fprintf(stderr, "When outputting to file, a minimal"
1051 					" user mapping proposal\n");
1052 			fprintf(stderr, "is written to the file, and no"
1053 					" partition should be mentioned\n");
1054 		}
1055 	}
1056 #else
1057 	err = (argc < 2);
1058 	if (err) {
1059 		fprintf(stderr, "Usage : ntfsusermap dev1 [dev2 ...]\n");
1060 		fprintf(stderr, "    \"dev.\" are the devices to share"
1061 				" with Windows\n");
1062 		fprintf(stderr, "        eg /dev/sdb1\n");
1063 		fprintf(stderr, "    the devices should not be mounted, and\n");
1064 		fprintf(stderr, "    the Windows system partition should"
1065 				" be named first\n");
1066 	} else
1067 		if (getuid()) {
1068 			fprintf(stderr, "\nSorry, only root can start"
1069 					" ntfsusermap\n");
1070 			err = AGREED;
1071 		}
1072 #endif
1073 	return (!err);
1074 }
1075 
process(int argc,char * argv[])1076 static boolean process(int argc, char *argv[])
1077 {
1078 	boolean ok;
1079 	int xarg;
1080 	int targ;
1081 
1082 	firstmapping = (struct USERMAPPING *)NULL;
1083 	lastmapping = (struct USERMAPPING *)NULL;
1084 	ok = AGREED;
1085 	for (xarg=1; (xarg<argc) && ok; xarg++)
1086 		if (open_volume(argv[xarg])) {
1087 			printf("\n* Scanning \"%s\" (two levels)\n",argv[xarg]);
1088 			ok = getusers("/",2);
1089 			close_volume(argv[xarg]);
1090 		} else
1091 			ok = DENIED;
1092 	if (ok && sanitize()) {
1093 		targ = (argc > 2 ? 2 : 1);
1094 		if (!outputmap(argv[targ],MAPDIR)) {
1095 			printf("Trying to write file on root directory\n");
1096 			if (outputmap(argv[targ],(const char*)NULL)) {
1097 				printf("\nNote : you will have to move the"
1098 					" file to directory \"%s\" on Linux\n",
1099 					MAPDIR);
1100 			} else
1101 				ok = DENIED;
1102 		} else
1103 			ok = DENIED;
1104 	} else
1105 		ok = DENIED;
1106 	return (ok);
1107 }
1108 
main(int argc,char * argv[])1109 int main(int argc, char *argv[])
1110 {
1111 	boolean ok;
1112 	boolean silent;
1113 
1114 	silent = !isatty(1);
1115 	if (!silent)
1116 		welcome();
1117 	if (checkoptions(argc, argv, silent)) {
1118 #ifdef HAVE_WINDOWS_H
1119 		loginname(silent);
1120 		if (silent)
1121 			ok = minimal(currentsid);
1122 		else
1123 			ok = process(argc,argv);
1124 #else
1125 		ok = process(argc,argv);
1126 #endif
1127 	} else
1128 		ok = DENIED;
1129 	if (!ok)
1130 		exit(1);
1131 	return (0);
1132 }
1133