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