blob: 19c5c9065159bbb64311e0802103d8ae91036ea6 [file] [log] [blame]
Steve Kondik2111ad72013-07-07 12:07:44 -07001/*
2 * Windows to Linux user mapping for ntfs-3g
3 *
4 *
Steve Kondik79165c32015-11-09 19:43:00 -08005 * Copyright (c) 2007-2014 Jean-Pierre Andre
Steve Kondik2111ad72013-07-07 12:07:44 -07006 *
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
Steve Kondik79165c32015-11-09 19:43:00 -080044 * - silenced compiler warnings for unused parameters
Steve Kondik2111ad72013-07-07 12:07:44 -070045 *
46 * Jan 2010 Version 1.1.4
47 * - fixed compilation problems for Mac OSX (Erik Larsson)
Steve Kondik79165c32015-11-09 19:43:00 -080048 *
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
Steve Kondik2111ad72013-07-07 12:07:44 -070054 */
55
56/*
57 * This program is free software; you can redistribute it and/or modify
58 * it under the terms of the GNU General Public License as published by
59 * the Free Software Foundation; either version 2 of the License, or
60 * (at your option) any later version.
61 *
62 * This program is distributed in the hope that it will be useful,
63 * but WITHOUT ANY WARRANTY; without even the implied warranty of
64 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
65 * GNU General Public License for more details.
66 *
67 * You should have received a copy of the GNU General Public License
68 * along with this program (in the main directory of the NTFS-3G
69 * distribution in the file COPYING); if not, write to the Free Software
70 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
71 */
72
73/*
74 * General parameters which may have to be adapted to needs
75 */
76
77#ifdef HAVE_CONFIG_H
78#define USESTUBS 1 /* API stubs generated at link time */
79#else
80#define USESTUBS 0 /* direct calls to API, based on following definitions */
81#define ENVNTFS3G "NTFS3G"
82#define LIBFILE64 "/lib64/libntfs-3g.so.491"
83#define LIBFILE "/lib/libntfs-3g.so.491"
84#endif
85
86#define GET_FILE_SECURITY "ntfs_get_file_security"
87#define SET_FILE_SECURITY "ntfs_set_file_security"
88#define READ_DIRECTORY "ntfs_read_directory"
89#define INIT_FILE_SECURITY "ntfs_initialize_file_security"
90#define LEAVE_FILE_SECURITY "ntfs_leave_file_security"
91
Steve Kondik79165c32015-11-09 19:43:00 -080092#define VERSION "1.1.6"
Steve Kondik2111ad72013-07-07 12:07:44 -070093#define MAPDIR ".NTFS-3G"
94#define MAPFILE "UserMapping"
95#define MAXATTRSZ 2048
96#define MAXSIDSZ 80
97#define MAXNAMESZ 256
98#define OWNERS1 "Documents and Settings"
99#define OWNERS2 "Users"
100
101/*
102 * Define WIN32 for a Windows execution
103 * may have to be adapted to compiler or something else
104 */
105
106#ifndef WIN32
107#if defined(__WIN32) | defined(__WIN32__) | defined(WNSC)
108#define WIN32 1
109#endif
110#endif
111
112#ifdef WIN32
113#define BANNER "Generated by usermap for Windows, v " VERSION
114#else
115#define BANNER "Generated by usermap for Linux, v " VERSION
116#endif
117
118
119#include <stdio.h>
120#include <string.h>
121#include <stdlib.h>
122#include <fcntl.h>
123#include <sys/types.h>
124#include <sys/stat.h>
125#include <errno.h>
126
127/*
128 * Define the security API according to platform
129 */
130
131#ifdef WIN32
132
133#include <fcntl.h>
134#include <windows.h>
135
136#define STATIC
137
138typedef enum { DENIED, AGREED } boolean;
139
140#else
141
142#include <unistd.h>
143#include <dlfcn.h>
144
145typedef enum { DENIED, AGREED } boolean, BOOL;
146typedef unsigned int DWORD; /* must be 32 bits whatever the platform */
147typedef DWORD *LPDWORD;
148
149enum { OWNER_SECURITY_INFORMATION = 1,
150 GROUP_SECURITY_INFORMATION = 2,
151 DACL_SECURITY_INFORMATION = 4,
152 SACL_SECURITY_INFORMATION = 8
153} ;
154
155struct CALLBACK {
156 const char *accname;
157 const char *dir;
158 int levels;
159 int docset;
160} ;
161
162typedef int (*dircallback)(struct CALLBACK *context, char *ntfsname,
163 int length, int type, long long pos, unsigned long long mft_ref,
164 unsigned int dt_type);
165
166#if USESTUBS
167
168#define STATIC static
169
170BOOL ntfs_get_file_security(void *scapi,
171 const char *path, DWORD selection,
172 char *buf, DWORD buflen, LPDWORD psize);
173BOOL ntfs_set_file_security(void *scapi,
174 const char *path, DWORD selection, const char *attr);
175BOOL ntfs_read_directory(void *scapi,
176 const char *path, dircallback callback, void *context);
177void *ntfs_initialize_file_security(const char *device,
Steve Kondik79165c32015-11-09 19:43:00 -0800178 unsigned long flags);
Steve Kondik2111ad72013-07-07 12:07:44 -0700179BOOL ntfs_leave_file_security(void *scapi);
180
181#else
182
183#define STATIC
184
185BOOL (*ntfs_get_file_security)(void *scapi,
186 const char *path, DWORD selection,
187 char *buf, DWORD buflen, LPDWORD psize);
188BOOL (*ntfs_set_file_security)(void *scapi,
189 const char *path, DWORD selection, const char *attr);
190BOOL (*ntfs_read_directory)(void *scapi,
191 const char *path, dircallback callback, void *context);
192void *(*ntfs_initialize_file_security)(const char *device,
Steve Kondik79165c32015-11-09 19:43:00 -0800193 unsigned long flags);
Steve Kondik2111ad72013-07-07 12:07:44 -0700194BOOL (*ntfs_leave_file_security)(void *scapi);
195
196#endif
197
198STATIC boolean open_security_api(void);
199STATIC boolean close_security_api(void);
200STATIC boolean open_volume(const char *volume);
201STATIC boolean close_volume(const char *volume);
202
203#endif
204
205struct MAPPING {
206 struct MAPPING *next;
207 const char *uidstr;
208 const char *gidstr;
209 const char *sidstr;
210 const unsigned char *sid;
211 const char *login;
212 boolean defined;
213};
214
215struct MAPPING *firstmapping;
216struct MAPPING *lastmapping;
217
218#ifdef WIN32
219char *currentwinname;
220char *currentdomain;
221unsigned char *currentsid;
222#endif
223
224#ifndef WIN32
225
226void *ntfs_handle;
227void *ntfs_context = (void*)NULL;
228
229/*
230 * Shut down compiler warnings for unused parameters
231 */
232
233static long unused(const void *p)
234{
235return ((long)p);
236}
237
238/*
239 * Open and close the security API (platform dependent)
240 */
241
242STATIC boolean open_security_api(void)
243{
244#if USESTUBS
245 return (AGREED);
246#else
247 char *error;
248 boolean err;
249 const char *libfile;
250
251 err = AGREED;
252 libfile = getenv(ENVNTFS3G);
253 if (!libfile)
254 libfile = (sizeof(char*) == 8 ? LIBFILE64 : LIBFILE);
255 ntfs_handle = dlopen(libfile,RTLD_LAZY);
256 if (ntfs_handle) {
257 ntfs_initialize_file_security =
258 dlsym(ntfs_handle,INIT_FILE_SECURITY);
259 error = dlerror();
260 if (error)
261 fprintf(stderr," %s\n",error);
262 else {
263 ntfs_leave_file_security =
264 dlsym(ntfs_handle,LEAVE_FILE_SECURITY);
265 ntfs_get_file_security =
266 dlsym(ntfs_handle,GET_FILE_SECURITY);
267 ntfs_set_file_security =
268 dlsym(ntfs_handle,SET_FILE_SECURITY);
269 ntfs_read_directory =
270 dlsym(ntfs_handle,READ_DIRECTORY);
271 err = !ntfs_initialize_file_security
272 || !ntfs_leave_file_security
273 || !ntfs_get_file_security
274 || !ntfs_set_file_security
275 || !ntfs_read_directory;
276 if (error)
277 fprintf(stderr,"ntfs-3g API not available\n");
278 }
279 } else {
280 fprintf(stderr,"Could not open ntfs-3g library\n");
281 fprintf(stderr,"\nPlease set environment variable \"" ENVNTFS3G "\"\n");
282 fprintf(stderr,"to appropriate path and retry\n");
283 }
284 return (!err);
285#endif
286}
287
288STATIC boolean close_security_api(void)
289{
290#if USESTUBS
291 return (0);
292#else
293 return (!dlclose(ntfs_handle));
294#endif
295}
296
297/*
298 * Open and close a volume (platform dependent)
299 * assuming a single volume needs to be opened at any time
300 */
301
302STATIC boolean open_volume(const char *volume)
303{
304 boolean ok;
305
306 ok = DENIED;
307 if (!ntfs_context) {
308 ntfs_context = ntfs_initialize_file_security(volume,0);
309 if (ntfs_context) {
310 fprintf(stderr,"\"%s\" opened\n",volume);
311 ok = AGREED;
312 } else {
313 fprintf(stderr,"Could not open \"%s\"\n",volume);
314 fprintf(stderr,"Make sure \"%s\" is not mounted\n",volume);
315 }
316 } else
317 fprintf(stderr,"A volume is already open\n");
318 return (ok);
319}
320
321STATIC boolean close_volume(const char *volume)
322{
323 boolean r;
324
325 r = ntfs_leave_file_security(ntfs_context);
326 if (r)
327 fprintf(stderr,"\"%s\" closed\n",volume);
328 else
329 fprintf(stderr,"Could not close \"%s\"\n",volume);
330 ntfs_context = (void*)NULL;
331 return (r);
332}
333
334/*
335 * A poor man's conversion of Unicode to UTF8
336 * We are assuming outputs to terminal expect UTF8
337 */
338
339STATIC void to_utf8(char *dst, const char *src, unsigned int cnt)
340{
341 unsigned int ch;
342 unsigned int i;
343
344 for (i=0; i<cnt; i++) {
345 ch = *src++ & 255;
346 ch += (*src++ & 255) << 8;
347 if (ch < 0x80)
348 *dst++ = ch;
349 else
350 if (ch < 0x1000) {
351 *dst++ = 0xc0 + (ch >> 6);
352 *dst++ = 0x80 + (ch & 63);
353 } else {
354 *dst++ = 0xe0 + (ch >> 12);
355 *dst++ = 0x80 + ((ch >> 6) & 63);
356 *dst++ = 0x80 + (ch & 63);
357 }
358 }
359 *dst = 0;
360}
361
362STATIC int utf8_size(const char *src, unsigned int cnt)
363{
364 unsigned int ch;
365 unsigned int i;
366 int size;
367
368 size = 0;
369 for (i=0; i<cnt; i++) {
370 ch = *src++ & 255;
371 ch += (*src++ & 255) << 8;
372 if (ch < 0x80)
373 size++;
374 else
375 if (ch < 0x1000)
376 size += 2;
377 else
378 size += 3;
379 }
380 return (size);
381}
382
383#endif
384
385
386STATIC void welcome(void)
387{
388 printf("\nThis tool will help you to build a mapping of Windows users\n");
389 printf("to Linux users.\n");
390 printf("Be prepared to give Linux user id (uid) and group id (gid)\n");
391 printf("for owners of files which will be selected.\n");
392}
393
394STATIC unsigned int get2l(const unsigned char *attr, int p)
395{
396 int i;
397 unsigned int v;
398
399 v = 0;
400 for (i = 0; i < 2; i++)
401 v += (attr[p + i] & 255) << (8 * i);
402 return (v);
403}
404
405STATIC unsigned long get4l(const unsigned char *attr, int p)
406{
407 int i;
408 unsigned long v;
409
410 v = 0;
411 for (i = 0; i < 4; i++)
412 v += (attr[p + i] & 255L) << (8 * i);
413 return (v);
414}
415
416STATIC unsigned long long get6h(const unsigned char *attr, int p)
417{
418 int i;
419 unsigned long long v;
420
421 v = 0;
422 for (i = 0; i < 6; i++)
423 v = (v << 8) + (attr[p + i] & 255L);
424 return (v);
425}
426
427STATIC char *decodesid(const unsigned char *sid)
428{
429 char *str;
430 int i;
431 unsigned long long auth;
432 unsigned long subauth;
433
434 str = (char *)malloc(MAXSIDSZ);
435 if (str) {
436 strcpy(str, "S");
437 sprintf(&str[strlen(str)], "-%d", sid[0]); /* revision */
438 auth = get6h(sid, 2);
439#ifdef WIN32
440 sprintf(&str[strlen(str)], "-%I64u", auth); /* main authority */
441#else
442 sprintf(&str[strlen(str)], "-%llu", auth); /* main authority */
443#endif
444 for (i = 0; (i < 8) && (i < sid[1]); i++) {
445 subauth = get4l(sid, 8 + 4 * i);
446 sprintf(&str[strlen(str)], "-%lu", subauth); /* sub-authority */
447 }
448 }
449 return (str);
450}
451
452/*
453 * Test whether a generic group (S-1-5-21- ... -513)
454 */
455
456STATIC boolean isgenericgroup(const char *sid)
457{
458 boolean yes;
459
460 yes = !strncmp(sid,"S-1-5-21-",9)
461 && !strcmp(strrchr(sid,'-'),"-513");
462 return (yes);
463}
464
465STATIC unsigned char *makegroupsid(const unsigned char *sid)
466{
467 static unsigned char groupsid[MAXSIDSZ];
468 int size;
469
470 size = 8 + 4*sid[1];
471 memcpy(groupsid, sid, size);
472 /* replace last level by 513 */
473 groupsid[size - 4] = 1;
474 groupsid[size - 3] = 2;
475 groupsid[size - 2] = 0;
476 groupsid[size - 1] = 0;
477 return (groupsid);
478}
479
480STATIC void domapping(const char *accname, const char *filename,
Steve Kondik79165c32015-11-09 19:43:00 -0800481 const char *dir, const unsigned char *sid, int type)
Steve Kondik2111ad72013-07-07 12:07:44 -0700482{
483 char buf[81];
484 char *sidstr;
485 char *idstr;
486 int sidsz;
487 boolean reject;
488 struct MAPPING *mapping;
489 char *login;
490 char *p;
491
492 if ((get6h(sid, 2) == 5) && (get4l(sid, 8) == 21)) {
493 sidstr = decodesid(sid);
494 mapping = firstmapping;
495 while (mapping && strcmp(mapping->sidstr, sidstr))
496 mapping = mapping->next;
497 if (mapping
498 && (mapping->defined
499 || !accname
500 || !strcmp(mapping->login, accname)))
501 free(sidstr); /* decision already known */
502 else {
503 do {
504 reject = DENIED;
505 printf("\n");
506 if (accname)
507 printf("Under Windows login \"%s\"\n", accname);
Steve Kondik79165c32015-11-09 19:43:00 -0800508 if (dir)
509 printf(" in directory \"%s\"\n",dir);
Steve Kondik2111ad72013-07-07 12:07:44 -0700510 printf(" file \"%s\" has no mapped %s\n",
511 filename,(type ? "group" : "owner"));
512 printf("By which Linux login should this file be owned ?\n");
513 printf("Enter %s of login, or just press \"enter\" if this file\n",
514 (type ? "gid" : "uid"));
515 printf("does not belong to a user, or you do not known to whom\n");
516 printf("\n");
517 if (type)
518 printf("Group : ");
519 else
520 printf("User : ");
521 p = fgets(buf, 80, stdin);
522 if (p && p[0] && (p[strlen(p) - 1] == '\n'))
523 p[strlen(p) - 1] = '\0';
524
525 if (p && p[0]
526 && ((p[0] == '0') || !strcmp(p, "root"))) {
527 printf("Please do not map users to root\n");
528 printf("Administrators will be mapped automatically\n");
529 reject = AGREED;
530 }
531 if (reject)
532 printf("Please retry\n");
533 } while (reject);
534 if (!mapping) {
535 mapping =
536 (struct MAPPING *)
537 malloc(sizeof(struct MAPPING));
538 mapping->next = (struct MAPPING *)NULL;
539 mapping->defined = DENIED;
540 if (lastmapping)
541 lastmapping->next = mapping;
542 else
543 firstmapping = mapping;
544 lastmapping = mapping;
545 }
546 if (mapping) {
547 if (p && p[0]) {
548 idstr = (char *)malloc(strlen(p) + 1);
549 if (idstr) {
550 strcpy(idstr, p);
551 if (type) {
552 mapping->uidstr = "";
553 mapping->gidstr = idstr;
554 } else {
555 mapping->uidstr = idstr;
556 mapping->gidstr = idstr;
557 }
558 mapping->defined = AGREED;
559 }
560 }
561 mapping->sidstr = sidstr;
562 if (accname) {
563 login = (char*)malloc(strlen(accname) + 1);
564 if (login)
565 strcpy(login,accname);
566 mapping->login = login;
567 } else
568 mapping->login = (char*)NULL;
569 sidsz = 8 + sid[1]*4;
570 p = (char*)malloc(sidsz);
571 if (p) {
572 memcpy(p, sid, sidsz);
573 }
574 mapping->sid = (unsigned char*)p;
575 }
576 }
577 }
578}
579
580STATIC void listaclusers(const char *accname, const unsigned char *attr, int off)
581{
582 int i;
583 int cnt;
584 int x;
585
586 cnt = get2l(attr, off + 4);
587 x = 8;
588 for (i = 0; i < cnt; i++) {
Steve Kondik79165c32015-11-09 19:43:00 -0800589 domapping(accname, (char *)NULL, (char*)NULL,
590 &attr[off + x + 8], 2);
Steve Kondik2111ad72013-07-07 12:07:44 -0700591 x += get2l(attr, off + x + 2);
592 }
593}
594
595#ifdef WIN32
596
597STATIC void account(const char *accname, const char *dir, const char *name, int type)
598{
599 unsigned char attr[MAXATTRSZ];
600 unsigned long attrsz;
601 char *fullname;
602 int attrib;
603
604 fullname = (char *)malloc(strlen(dir) + strlen(name) + 2);
605 if (fullname) {
606 strcpy(fullname, dir);
607 strcat(fullname, "\\");
608 strcat(fullname, name);
609 attrib = GetFileAttributes(fullname);
610 if (attrib & 0x10) { /* only directories processed */
611 if (GetFileSecurity
612 (fullname, OWNER_SECURITY_INFORMATION, attr, MAXATTRSZ,
613 &attrsz)) {
Steve Kondik79165c32015-11-09 19:43:00 -0800614 domapping(accname, name, dir, &attr[20], 0);
Steve Kondik2111ad72013-07-07 12:07:44 -0700615 attrsz = 0;
616 if (GetFileSecurity
617 (fullname, GROUP_SECURITY_INFORMATION, attr,
618 MAXATTRSZ, &attrsz))
Steve Kondik79165c32015-11-09 19:43:00 -0800619 domapping(accname, name, dir, &attr[20], 1);
Steve Kondik2111ad72013-07-07 12:07:44 -0700620 else
621 printf(" No group SID\n");
622 attrsz = 0;
623 if (GetFileSecurityA
624 (fullname, DACL_SECURITY_INFORMATION, attr,
625 MAXATTRSZ, &attrsz)) {
626 if (type == 0)
627 listaclusers(accname, attr, 20);
628 } else
629 printf
630 (" No discretionary access control list\n");
631 }
632 }
633 free(fullname);
634 }
635}
636
637#else
638
639STATIC void account(const char *accname, const char *dir, const char *name, int type)
640{
641 unsigned char attr[MAXATTRSZ];
642 DWORD attrsz;
643 char *fullname;
644
645 fullname = (char *)malloc(strlen(dir) + strlen(name) + 2);
646 if (fullname) {
647 strcpy(fullname, dir);
648 strcat(fullname, "/");
649 strcat(fullname, name);
650 if (ntfs_get_file_security(ntfs_context,
651 fullname, OWNER_SECURITY_INFORMATION,
652 (char*)attr, MAXATTRSZ, &attrsz)) {
Steve Kondik79165c32015-11-09 19:43:00 -0800653 domapping(accname, name, dir, &attr[20], 0);
Steve Kondik2111ad72013-07-07 12:07:44 -0700654 attrsz = 0;
655 if (ntfs_get_file_security(ntfs_context,
656 fullname, GROUP_SECURITY_INFORMATION,
657 (char*)attr, MAXATTRSZ, &attrsz))
Steve Kondik79165c32015-11-09 19:43:00 -0800658 domapping(accname, name, dir, &attr[20], 1);
Steve Kondik2111ad72013-07-07 12:07:44 -0700659 else
660 printf(" No group SID\n");
661 attrsz = 0;
662 if (ntfs_get_file_security(ntfs_context,
663 fullname, DACL_SECURITY_INFORMATION,
664 (char*)attr, MAXATTRSZ, &attrsz)) {
665 if (type == 0)
666 listaclusers(accname, attr, 20);
667 } else
668 printf(" No discretionary access control list for %s !\n",
669 dir);
670 }
671 free(fullname);
672 }
673}
674
675#endif
676
677
678/*
679 * recursive search of file owners and groups in a directory
680 */
681
682#ifdef WIN32
683
684STATIC boolean recurse(const char *accname, const char *dir, int levels)
685{
686 WIN32_FIND_DATA found;
687 HANDLE search;
688 char *filter;
689 char *fullname;
690 boolean err;
691
692 err = DENIED;
693 filter = (char *)malloc(strlen(dir) + 5);
694 if (filter) {
695 strcpy(filter, dir);
696 strcat(filter, "\\*.*");
697 search = FindFirstFile(filter, &found);
698 if (search != INVALID_HANDLE_VALUE) {
699 do {
700 if (found.cFileName[0] != '.') {
701 account(accname, dir, found.cFileName,1);
702 if (levels > 0) {
703 fullname =
704 (char *)malloc(strlen(dir) +
705 strlen(found.cFileName)
706 + 2);
707 if (fullname) {
708 strcpy(fullname, dir);
709 strcat(fullname, "\\");
710 strcat(fullname,
711 found.cFileName);
712 recurse(accname,
713 fullname,
714 levels - 1);
715 free(fullname);
716 }
717 }
718 }
719 } while (FindNextFile(search, &found));
720 FindClose(search);
721 }
722 free(filter);
723 } else {
724 printf("Directory %s not found\n",dir);
725 err = AGREED;
726 }
727 return (!err);
728}
729
730#else
731
732STATIC boolean recurse(const char *accname, const char *dir, int levels, int docset);
733
734STATIC int callback(struct CALLBACK *context, char *ntfsname,
735 int length, int type, long long pos, unsigned long long mft_ref,
736 unsigned int dt_type)
737{
738 char *fullname;
739 char *accname;
740 char *name;
741
742 unused((void*)&pos);
743 unused((void*)&mft_ref);
744 unused((void*)&dt_type);
745 fullname = (char *)malloc(strlen(context->dir)
746 + utf8_size(ntfsname, length) + 2);
747 if (fullname) {
748 if (strcmp(context->dir,"/")) {
749 strcpy(fullname, context->dir);
750 strcat(fullname, "/");
751 } else
752 strcpy(fullname,"/");
753 /* Unicode to ascii conversion by a lazy man */
754 name = &fullname[strlen(fullname)];
755 to_utf8(name, ntfsname, length);
756 /* ignore special files and DOS names */
757 if ((type != 2)
758 && strcmp(name,".")
759 && strcmp(name,"..")
760 && (name[0] != '$')) {
761 switch (context->docset) {
762 case 2 :
763 /*
764 * only "Documents and Settings"
765 * or "Users"
766 */
767 if (!strcmp(name,OWNERS1)
768 || !strcmp(name,OWNERS2)) {
769 recurse((char*)NULL, fullname, 2, 1);
770 }
771 break;
772 /*
773 * within "Documents and Settings"
774 * or "Users"
775 */
776 case 1 :
777 accname = (char*)malloc(strlen(name) + 1);
778 if (accname) {
779 strcpy(accname, name);
780 if (context->levels > 0)
781 recurse(name, fullname,
782 context->levels - 1, 0);
783 }
784 break;
785 /*
786 * not related to "Documents and Settings"
787 * or "Users"
788 */
789 case 0 :
790 account(context->accname, context->dir,
791 name, 1);
792 if (context->levels > 0)
793 recurse(context->accname, fullname,
794 context->levels - 1, 0);
795 break;
796 }
797 }
798 free(fullname);
799 }
800/* check expected return value */
801 return (0);
802}
803
804STATIC boolean recurse(const char *accname, const char *dir, int levels, int docset)
805{
806 struct CALLBACK context;
807 boolean err;
808
809 err = DENIED;
810 context.dir = dir;
811 context.accname = accname;
812 context.levels = levels;
813 context.docset = docset;
814 ntfs_read_directory(ntfs_context,dir,callback,&context);
815 return (!err);
816}
817#endif
818
819/*
820 * Search directory "Documents and Settings" for user accounts
821 */
822
823#ifdef WIN32
824
825STATIC boolean getusers(const char *dir, int levels)
826{
827 WIN32_FIND_DATA found;
828 HANDLE search;
829 char *filter;
830 char *fullname;
831 char *accname;
832 boolean err;
833 const char *docset;
834
835 /* first get files from "Documents and Settings" */
836 err = DENIED;
837 if (sizeof(OWNERS1) > sizeof(OWNERS2))
838 filter = (char *)malloc(strlen(dir) + strlen(OWNERS1) + 6);
839 else
840 filter = (char *)malloc(strlen(dir) + strlen(OWNERS2) + 6);
841 if (filter) {
842 docset = OWNERS1;
843 strcpy(filter, dir);
844 strcat(filter, "\\");
845 strcat(filter, docset);
846 strcat(filter, "\\*.*");
847 search = FindFirstFile(filter, &found);
848 /* if failed, retry with "Users" */
849 if (search == INVALID_HANDLE_VALUE) {
850 docset = OWNERS2;
851 strcpy(filter, dir);
852 strcat(filter, "\\");
853 strcat(filter, docset);
854 strcat(filter, "\\*.*");
855 search = FindFirstFile(filter, &found);
856 }
857 if (search != INVALID_HANDLE_VALUE) {
858 do {
859 if (found.cFileName[0] != '.') {
860 fullname =
861 (char *)malloc(strlen(dir)
862 + strlen(docset)
863 + strlen(found.cFileName) + 3);
864 accname = (char *)
865 malloc(strlen(found.cFileName) + 1);
866 if (fullname && accname) {
867 strcpy(accname,
868 found.cFileName);
869
870 strcpy(fullname, dir);
871 strcat(fullname, "\\");
872 strcat(fullname, docset);
873 strcat(fullname, "\\");
874 strcat(fullname,
875 found.cFileName);
876 recurse(accname, fullname, 2);
877
878 free(fullname);
879 }
880 }
881 } while (FindNextFile(search, &found));
882 FindClose(search);
883 } else {
884 printf("No subdirectory found in %s\\%s\n",dir,docset);
885 }
886 /* now search in other directories */
887 strcpy(filter, dir);
888 strcat(filter, "\\*.*");
889 search = FindFirstFile(filter, &found);
890 if (search != INVALID_HANDLE_VALUE) {
891 do {
892 if ((found.cFileName[0] != '.')
893 && strcmp(found.cFileName,OWNERS1)
894 && strcmp(found.cFileName,OWNERS2)) {
895 fullname =
896 (char *)malloc(strlen(dir)
897 + strlen(found.cFileName) + 2);
898 if (fullname) {
899 strcpy(fullname, dir);
900 strcat(fullname, "\\");
901 strcat(fullname,
902 found.cFileName);
903 recurse((char*)NULL, fullname, 2);
904 free(fullname);
905 }
906 }
907 } while (FindNextFile(search, &found));
908 FindClose(search);
909 } else {
910 printf("No directory found in %s\n",dir);
911 err = AGREED;
912 }
913 }
914 return (!err);
915}
916
917#else
918
919STATIC boolean getusers(const char *dir, int levels)
920{
921 boolean err;
922 struct CALLBACK context;
923
924 printf("* Search for \"" OWNERS1 "\" and \"" OWNERS2 "\"\n");
925 err = DENIED;
926 context.dir = dir;
927 context.accname = (const char*)NULL;
928 context.levels = levels;
929 context.docset = 2;
930 ntfs_read_directory(ntfs_context,dir,callback,&context);
931 printf("* Search for other directories %s\n",dir);
932 context.docset = 0;
933 ntfs_read_directory(ntfs_context,dir,callback,&context);
934
935 return (!err);
936}
937
938#endif
939
940#ifdef WIN32
941/*
942 * Get the current login name (Win32 only)
943 */
944
945STATIC void loginname(boolean silent)
946{
947 char *winname;
948 char *domain;
949 unsigned char *sid;
950 unsigned long namesz;
951 unsigned long sidsz;
952 unsigned long domainsz;
953 int nametype;
954 boolean ok;
955 int r;
956
957 ok = FALSE;
958 winname = (char*)malloc(MAXNAMESZ);
959 domain = (char*)malloc(MAXNAMESZ);
960 sid = (char*)malloc(MAXSIDSZ);
961
962 namesz = MAXNAMESZ;
963 domainsz = MAXNAMESZ;
964 sidsz = MAXSIDSZ;
965 if (winname
966 && domain
967 && sid
968 && GetUserName(winname,&namesz)) {
969 winname[namesz] = '\0';
970 if (!silent)
971 printf("Your current user name is %s\n",winname);
972 nametype = 1;
973 r = LookupAccountName((char*)NULL,winname,sid,&sidsz,
974 domain,&domainsz,&nametype);
975 if (r) {
976 domain[domainsz] = '\0';
977 if (!silent)
978 printf("Your account domain is %s\n",domain);
979 ok = AGREED;
980 }
981 }
982 if (ok) {
983 currentwinname = winname;
984 currentdomain = domain;
985 currentsid = sid;
986 } else {
987 currentwinname = (char*)NULL;
988 currentdomain = (char*)NULL;
989 currentsid = (unsigned char*)NULL;
990 }
991}
992
993/*
994 * Minimal output on stdout
995 */
996
997boolean minimal(unsigned char *sid)
998{
999 const unsigned char *groupsid;
1000 boolean ok;
1001
1002 ok = DENIED;
1003 if (sid) {
1004 groupsid = makegroupsid(sid);
1005 printf("# %s\n",BANNER);
1006 printf("# For Windows account \"%s\" in domain \"%s\"\n",
1007 currentwinname, currentdomain);
1008 printf("# Replace \"user\" and \"group\" hereafter by matching Linux login\n");
1009 printf("user::%s\n",decodesid(sid));
1010 printf(":group:%s\n",decodesid(groupsid));
1011 ok = AGREED;
1012 }
1013 return (ok);
1014}
1015
1016#endif
1017
1018STATIC boolean outputmap(const char *volume, const char *dir)
1019{
1020 char buf[256];
1021 int fn;
1022 char *fullname;
1023 char *backup;
1024 struct MAPPING *mapping;
1025 boolean done;
1026 boolean err;
1027 boolean undecided;
1028#ifdef WIN32
1029#else
1030 struct stat st;
1031 int s;
1032#endif
1033
1034 done = DENIED;
1035 fullname = (char *)malloc(strlen(MAPFILE) + 1
1036 + strlen(volume) + 1
1037 + (dir ? strlen(dir) + 1 : 0));
1038 if (fullname) {
1039#ifdef WIN32
1040 strcpy(fullname, volume);
1041 if (dir && dir[0]) {
1042 strcat(fullname, "\\");
1043 strcat(fullname,dir);
1044 }
1045
1046 /* build directory, if not present */
1047 if (GetFileAttributes(fullname) & 0x80000000) {
1048 printf("* Creating directory %s\n", fullname);
1049 mkdir(fullname);
1050 }
1051
1052 strcat(fullname, "\\");
1053 strcat(fullname, MAPFILE);
1054 printf("\n");
1055
1056 if (!(GetFileAttributes(fullname) & 0x80000000)) {
1057 backup = (char*)malloc(strlen(fullname) + 5);
1058 strcpy(backup,fullname);
1059 strcat(backup,".bak");
1060 unlink(backup);
1061 if (!rename(fullname,backup))
1062 printf("* Old mapping file moved to %s\n",backup);
1063 }
1064#else
1065 strcpy(fullname, MAPFILE);
1066 printf("\n");
1067
1068 s = stat(fullname,&st);
1069 if (!s) {
1070 backup = (char*)malloc(strlen(fullname + 5));
1071 strcpy(backup,fullname);
1072 strcat(backup,".bak");
1073 if (rename(fullname,backup))
1074 printf("* Old mapping file moved to %s\n",backup);
1075 }
1076#endif
1077
1078 printf("* Creating file %s\n", fullname);
1079 err = DENIED;
1080#ifdef WIN32
1081 fn = open(fullname,O_CREAT + O_TRUNC + O_WRONLY + O_BINARY,
1082 S_IREAD + S_IWRITE);
1083#else
1084 fn = open(fullname,O_CREAT + O_TRUNC + O_WRONLY,
1085 S_IREAD + S_IWRITE);
1086#endif
1087 if (fn > 0) {
1088 sprintf(buf,"# %s\n",BANNER);
1089 if (!write(fn,buf,strlen(buf)))
1090 err = AGREED;
1091 printf("%s",buf);
1092 undecided = DENIED;
1093 /* records for owner only or group only */
1094 for (mapping = firstmapping; mapping && !err;
1095 mapping = mapping->next)
1096 if (mapping->defined
1097 && (!mapping->uidstr[0] || !mapping->gidstr[0])) {
1098 sprintf(buf,"%s:%s:%s\n",
1099 mapping->uidstr,
1100 mapping->gidstr,
1101 mapping->sidstr);
1102 if (!write(fn,buf,strlen(buf)))
1103 err = AGREED;
1104 printf("%s",buf);
1105 } else
1106 undecided = AGREED;
1107 /* records for both owner and group */
1108 for (mapping = firstmapping; mapping && !err;
1109 mapping = mapping->next)
1110 if (mapping->defined
1111 && mapping->uidstr[0] && mapping->gidstr[0]) {
1112 sprintf(buf,"%s:%s:%s\n",
1113 mapping->uidstr,
1114 mapping->gidstr,
1115 mapping->sidstr);
1116 if (!write(fn,buf,strlen(buf)))
1117 err = AGREED;
1118 printf("%s",buf);
1119 } else
1120 undecided = AGREED;
1121 done = !err;
1122 close(fn);
1123 if (undecided) {
1124 printf("Undecided :\n");
1125 for (mapping = firstmapping; mapping;
1126 mapping = mapping->next)
1127 if (!mapping->defined) {
1128 printf(" %s\n", mapping->sidstr);
1129 }
1130 }
1131#ifndef WIN32
1132 printf("\n* You will have to move the file \"" MAPFILE "\"\n");
1133 printf(" to directory \"" MAPDIR "\" after mounting\n");
1134#endif
1135 }
1136 }
1137 if (!done)
1138 fprintf(stderr, "* Could not create mapping file \"%s\"\n", fullname);
1139 return (done);
1140}
1141
1142STATIC boolean sanitize(void)
1143{
1144 char buf[81];
1145 boolean ok;
1146 int ownercnt;
1147 int groupcnt;
1148 struct MAPPING *mapping;
1149 struct MAPPING *firstowner;
1150 struct MAPPING *genericgroup;
1151 struct MAPPING *group;
1152 char *sidstr;
1153
1154 /* count owners and groups */
1155 /* and find first user, and a generic group */
1156 ownercnt = 0;
1157 groupcnt = 0;
1158 firstowner = (struct MAPPING*)NULL;
1159 genericgroup = (struct MAPPING*)NULL;
1160 for (mapping=firstmapping; mapping; mapping=mapping->next) {
1161 if (mapping->defined && mapping->uidstr[0]) {
1162 if (!ownercnt)
1163 firstowner = mapping;
1164 ownercnt++;
1165 }
1166 if (mapping->defined && mapping->gidstr[0] && !mapping->uidstr[0]) {
1167 groupcnt++;
1168 }
1169 if (!mapping->defined && isgenericgroup(mapping->sidstr)) {
1170 genericgroup = mapping;
1171 }
1172 }
1173#ifdef WIN32
1174 /* no user defined, on Windows, suggest a mapping */
1175 /* based on account currently used */
1176 if (!ownercnt && currentwinname && currentsid) {
1177 char *owner;
1178 char *p;
1179
1180 printf("\nYou have defined no file owner,\n");
1181 printf(" please enter the Linux login which should be mapped\n");
1182 printf(" to account you are currently using\n");
1183 printf(" Linux user ? ");
1184 p = fgets(buf, 80, stdin);
1185 if (p && p[0] && (p[strlen(p) - 1] == '\n'))
1186 p[strlen(p) - 1] = '\0';
1187 if (p && p[0]) {
1188 firstowner = (struct MAPPING*)malloc(sizeof(struct MAPPING));
1189 owner = (char*)malloc(strlen(p) + 1);
1190 if (firstowner && owner) {
1191 strcpy(owner, p);
1192 firstowner->next = firstmapping;
1193 firstowner->uidstr = owner;
1194 firstowner->gidstr = "";
1195 firstowner->sidstr = decodesid(currentsid);
1196 firstowner->sid = currentsid;
1197 firstmapping = firstowner;
1198 ownercnt++;
1199 /* prefer a generic group with the same authorities */
1200 for (mapping=firstmapping; mapping;
1201 mapping=mapping->next)
1202 if (!mapping->defined
1203 && isgenericgroup(mapping->sidstr)
1204 && !memcmp(firstowner->sidstr,
1205 mapping->sidstr,
1206 strlen(mapping->sidstr)-3))
1207 genericgroup = mapping;
1208 }
1209 }
1210 }
1211#endif
1212 if (ownercnt) {
1213 /*
1214 * No group was selected, but there were a generic group
1215 * insist in using it, associated to the first user
1216 */
1217 if (!groupcnt) {
1218 printf("\nYou have defined no group, this can cause problems\n");
1219 printf("Do you accept defining a standard group ?\n");
1220 if (!fgets(buf,80,stdin)
1221 || ((buf[0] != 'n')
1222 && (buf[0] != 'N'))) {
1223 if (genericgroup) {
1224 genericgroup->uidstr = "";
1225 genericgroup->gidstr = firstowner->uidstr;
1226 genericgroup->defined = AGREED;
1227 } else {
1228 group = (struct MAPPING*)
1229 malloc(sizeof(struct MAPPING));
1230 sidstr = decodesid(
1231 makegroupsid(firstowner->sid));
1232 if (group && sidstr) {
1233 group->uidstr = "";
1234 group->gidstr = firstowner->
1235 uidstr;
1236 group->sidstr = sidstr;
1237 group->defined = AGREED;
1238 group->next = firstmapping;
1239 firstmapping = group;
1240 }
1241 }
1242 }
1243 }
1244 ok = AGREED;
1245 } else {
1246 printf("\nYou have defined no user, no mapping can be built\n");
1247 ok = DENIED;
1248 }
1249
1250 return (ok);
1251}
1252
1253STATIC boolean checkoptions(int argc, char *argv[], boolean silent)
1254{
1255 boolean err;
1256#ifdef WIN32
1257 int xarg;
1258 const char *pvol;
1259
1260 if (silent)
1261 err = (argc != 1);
1262 else {
1263 err = (argc < 2);
1264 for (xarg=1; (xarg<argc) && !err; xarg++) {
1265 pvol = argv[xarg];
1266 if (pvol[0] && (pvol[1] == ':') && !pvol[2]) {
1267 err = !(((pvol[0] >= 'A') && (pvol[0] <= 'Z'))
1268 || ((pvol[0] >= 'a') && (pvol[0] <= 'z')));
1269 }
1270 }
1271 }
1272 if (err) {
1273 fprintf(stderr, "Usage : usermap [vol1: [vol2: ...]]\n");
1274 fprintf(stderr, " \"voln\" are the letters of the partition to share with Linux\n");
1275 fprintf(stderr, " eg C:\n");
1276 fprintf(stderr, " the Windows system partition should be named first\n");
1277 }
1278#else
1279 unused((void*)argv);
1280 unused((void*)&silent);
1281 err = (argc < 2);
1282 if (err) {
1283 fprintf(stderr, "Usage : usermap dev1 [dev2 ...]\n");
1284 fprintf(stderr, " \"dev.\" are the devices to share with Windows\n");
1285 fprintf(stderr, " eg /dev/sdb1\n");
1286 fprintf(stderr, " the devices should not be mounted\n");
1287 fprintf(stderr, " the Windows system partition should be named first\n");
1288 } else
1289 if (getuid()) {
1290 fprintf(stderr, "\nSorry, only root can start usermap\n");
1291 err = AGREED;
1292 }
1293#endif
1294 return (!err);
1295}
1296
1297STATIC boolean process(int argc, char *argv[])
1298{
1299 boolean ok;
1300 int xarg;
1301 int targ;
1302
1303 firstmapping = (struct MAPPING *)NULL;
1304 lastmapping = (struct MAPPING *)NULL;
1305 ok = AGREED;
1306#ifdef WIN32
1307 for (xarg=1; (xarg<argc) && ok; xarg++) {
1308 printf("\n* Scanning \"%s\" (two levels)\n",argv[xarg]);
1309 ok = getusers(argv[xarg],2);
1310 }
1311#else
1312 for (xarg=1; (xarg<argc) && ok; xarg++)
1313 if (open_volume(argv[xarg])) {
1314 printf("\n* Scanning \"%s\" (two levels)\n",argv[xarg]);
1315 ok = getusers("/",2);
1316 close_volume(argv[xarg]);
1317 } else
1318 ok = DENIED;
1319#endif
1320 if (ok && sanitize()) {
1321 targ = (argc > 2 ? 2 : 1);
1322 if (!outputmap(argv[targ],MAPDIR)) {
1323 printf("Trying to write file on root directory\n");
1324 if (outputmap(argv[targ],(const char*)NULL)) {
1325 printf("\nNote : you will have to move the file to directory \"%s\" on Linux\n",
1326 MAPDIR);
1327 } else
1328 ok = DENIED;
1329 } else
1330 ok = DENIED;
1331 } else
1332 ok = DENIED;
1333 return (ok);
1334}
1335
1336int main(int argc, char *argv[])
1337{
1338 boolean ok;
1339 boolean silent;
1340
1341 silent = !isatty(1);
1342 if (!silent)
1343 welcome();
1344 if (checkoptions(argc, argv, silent)) {
1345#ifdef WIN32
1346 loginname(silent);
1347 if (silent)
1348 ok = minimal(currentsid);
1349 else
1350 ok = process(argc, argv);
1351#else
1352 if (open_security_api()) {
1353 ok = process(argc,argv);
1354 if (!close_security_api()) ok = DENIED;
1355 }
Steve Kondik79165c32015-11-09 19:43:00 -08001356 else {
1357 ok = DENIED;
1358 }
Steve Kondik2111ad72013-07-07 12:07:44 -07001359#endif
1360 } else
1361 ok = DENIED;
1362 if (!ok)
1363 exit(1);
1364 return (0);
1365}