ac98b992bb2187f7deb6ba01f4d2849b5fab24ea
[metze/old/v3-2-winbind-ndr.git] / source / client / mount.cifs.c
1 /* 
2    Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3    Copyright (C) 2003,2005 Steve French  (sfrench@us.ibm.com)
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9    
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14    
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
18
19 #ifndef _GNU_SOURCE
20 #define _GNU_SOURCE
21 #endif
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <pwd.h>
27 #include <grp.h>
28 #include <ctype.h>
29 #include <sys/types.h>
30 #include <sys/mount.h>
31 #include <sys/stat.h>
32 #include <sys/utsname.h>
33 #include <sys/socket.h>
34 #include <arpa/inet.h>
35 #include <getopt.h>
36 #include <errno.h>
37 #include <netdb.h>
38 #include <string.h>
39 #include <mntent.h>
40 #include <fcntl.h>
41
42 #define MOUNT_CIFS_VERSION_MAJOR "1"
43 #define MOUNT_CIFS_VERSION_MINOR "10"
44
45 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
46  #ifdef _SAMBA_BUILD_
47   #include "include/version.h"
48   #ifdef SAMBA_VERSION_VENDOR_SUFFIX
49    #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
50   #else
51    #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
52   #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
53  #else
54    #define MOUNT_CIFS_VENDOR_SUFFIX ""
55  #endif /* _SAMBA_BUILD_ */
56 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
57
58 #ifndef MS_MOVE 
59 #define MS_MOVE 8192 
60 #endif 
61
62 #define CONST_DISCARD(type, ptr)      ((type) ((void *) (ptr)))
63
64 const char *thisprogram;
65 int verboseflag = 0;
66 static int got_password = 0;
67 static int got_user = 0;
68 static int got_domain = 0;
69 static int got_ip = 0;
70 static int got_unc = 0;
71 static int got_uid = 0;
72 static int got_gid = 0;
73 static int free_share_name = 0;
74 static char * user_name = NULL;
75 static char * mountpassword = NULL;
76 char * domain_name = NULL;
77
78
79 /* BB finish BB
80
81         cifs_umount
82         open nofollow - avoid symlink exposure? 
83         get owner of dir see if matches self or if root
84         call system(umount argv) etc.
85                 
86 BB end finish BB */
87
88 static char * check_for_domain(char **);
89
90
91 static void mount_cifs_usage(void)
92 {
93         printf("\nUsage:  %s <remotetarget> <dir> -o <options>\n", thisprogram);
94         printf("\nMount the remote target, specified as a UNC name,");
95         printf(" to a local directory.\n\nOptions:\n");
96         printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
97         printf("\nLess commonly used options:");
98         printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
99         printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
100         printf("\n\tdirectio,mapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
101         printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
102         printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
103         printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
104         printf("\n\nRarely used options:");
105         printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
106         printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
107         printf("\n\tnointr,ignorecase,noposixpaths,noacl");
108         printf("\n\nOptions are described in more detail in the manual page");
109         printf("\n\tman 8 mount.cifs\n");
110         printf("\nTo display the version number of the mount helper:");
111         printf("\n\t%s -V\n",thisprogram);
112
113         if(mountpassword) {
114                 memset(mountpassword,0,64);
115                 free(mountpassword);
116         }
117         exit(1);
118 }
119
120 /* caller frees username if necessary */
121 static char * getusername(void) {
122         char *username = NULL;
123         struct passwd *password = getpwuid(getuid());
124
125         if (password) {
126                 username = password->pw_name;
127         }
128         return username;
129 }
130
131 static char * parse_cifs_url(char * unc_name)
132 {
133         printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
134         return NULL;
135 }
136
137 static int open_cred_file(char * file_name)
138 {
139         char * line_buf;
140         char * temp_val;
141         FILE * fs;
142         int i, length;
143         fs = fopen(file_name,"r");
144         if(fs == NULL)
145                 return errno;
146         line_buf = malloc(4096);
147         if(line_buf == NULL) {
148                 fclose(fs);
149                 return -ENOMEM;
150         }
151
152         while(fgets(line_buf,4096,fs)) {
153                 /* parse line from credential file */
154
155                 /* eat leading white space */
156                 for(i=0;i<4086;i++) {
157                         if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
158                                 break;
159                         /* if whitespace - skip past it */
160                 }
161                 if (strncasecmp("username",line_buf+i,8) == 0) {
162                         temp_val = strchr(line_buf + i,'=');
163                         if(temp_val) {
164                                 /* go past equals sign */
165                                 temp_val++;
166                                 for(length = 0;length<4087;length++) {
167                                         if(temp_val[length] == '\n')
168                                                 break;
169                                 }
170                                 if(length > 4086) {
171                                         printf("mount.cifs failed due to malformed username in credentials file");
172                                         memset(line_buf,0,4096);
173                                         if(mountpassword) {
174                                                 memset(mountpassword,0,64);
175                                         }
176                                         exit(1);
177                                 } else {
178                                         got_user = 1;
179                                         user_name = calloc(1 + length,1);
180                                         /* BB adding free of user_name string before exit,
181                                                 not really necessary but would be cleaner */
182                                         strncpy(user_name,temp_val, length);
183                                 }
184                         }
185                 } else if (strncasecmp("password",line_buf+i,8) == 0) {
186                         temp_val = strchr(line_buf+i,'=');
187                         if(temp_val) {
188                                 /* go past equals sign */
189                                 temp_val++;
190                                 for(length = 0;length<65;length++) {
191                                         if(temp_val[length] == '\n')
192                                                 break;
193                                 }
194                                 if(length > 64) {
195                                         printf("mount.cifs failed: password in credentials file too long\n");
196                                         memset(line_buf,0, 4096);
197                                         if(mountpassword) {
198                                                 memset(mountpassword,0,64);
199                                         }
200                                         exit(1);
201                                 } else {
202                                         if(mountpassword == NULL) {
203                                                 mountpassword = calloc(65,1);
204                                         } else
205                                                 memset(mountpassword,0,64);
206                                         if(mountpassword) {
207                                                 strncpy(mountpassword,temp_val,length);
208                                                 got_password = 1;
209                                         }
210                                 }
211                         }
212                 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
213                         temp_val = strchr(line_buf+i,'=');
214                         if(temp_val) {
215                                 /* go past equals sign */
216                                 temp_val++;
217                                 if(verboseflag)
218                                         printf("\nDomain %s\n",temp_val);
219                                 for(length = 0;length<65;length++) {
220                                         if(temp_val[length] == '\n')
221                                                 break;
222                                 }
223                                 if(length > 64) {
224                                         printf("mount.cifs failed: domain in credentials file too long\n");
225                                         if(mountpassword) {
226                                                 memset(mountpassword,0,64);
227                                         }
228                                         exit(1);
229                                 } else {
230                                         if(domain_name == NULL) {
231                                                 domain_name = calloc(65,1);
232                                         } else
233                                                 memset(domain_name,0,64);
234                                         if(domain_name) {
235                                                 strncpy(domain_name,temp_val,length);
236                                                 got_domain = 1;
237                                         }
238                                 }
239                         }
240                 }
241
242         }
243         fclose(fs);
244         if(line_buf) {
245                 memset(line_buf,0,4096);
246                 free(line_buf);
247         }
248         return 0;
249 }
250
251 static int get_password_from_file(int file_descript, char * filename)
252 {
253         int rc = 0;
254         int i;
255         char c;
256
257         if(mountpassword == NULL)
258                 mountpassword = calloc(65,1);
259         else 
260                 memset(mountpassword, 0, 64);
261
262         if (mountpassword == NULL) {
263                 printf("malloc failed\n");
264                 exit(1);
265         }
266
267         if(filename != NULL) {
268                 file_descript = open(filename, O_RDONLY);
269                 if(file_descript < 0) {
270                         printf("mount.cifs failed. %s attempting to open password file %s\n",
271                                    strerror(errno),filename);
272                         exit(1);
273                 }
274         }
275         /* else file already open and fd provided */
276
277         for(i=0;i<64;i++) {
278                 rc = read(file_descript,&c,1);
279                 if(rc < 0) {
280                         printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
281                         memset(mountpassword,0,64);
282                         if(filename != NULL)
283                                 close(file_descript);
284                         exit(1);
285                 } else if(rc == 0) {
286                         if(mountpassword[0] == 0) {
287                                 if(verboseflag)
288                                         printf("\nWarning: null password used since cifs password file empty");
289                         }
290                         break;
291                 } else /* read valid character */ {
292                         if((c == 0) || (c == '\n')) {
293                                 break;
294                         } else 
295                                 mountpassword[i] = c;
296                 }
297         }
298         if((i == 64) && (verboseflag)) {
299                 printf("\nWarning: password longer than 64 characters specified in cifs password file");
300         }
301         got_password = 1;
302         if(filename != NULL) {
303                 close(file_descript);
304         }
305
306         return rc;
307 }
308
309 static int parse_options(char ** optionsp, int * filesys_flags)
310 {
311         const char * data;
312         char * percent_char = NULL;
313         char * value = NULL;
314         char * next_keyword = NULL;
315         char * out = NULL;
316         int out_len = 0;
317         int word_len;
318         int rc = 0;
319
320         if (!optionsp || !*optionsp)
321                 return 1;
322         data = *optionsp;
323
324         if(verboseflag)
325                 printf("parsing options: %s\n", data);
326
327         /* BB fixme check for separator override BB */
328
329 /* while ((data = strsep(&options, ",")) != NULL) { */
330         while(data != NULL) {
331                 /*  check if ends with trailing comma */
332                 if(*data == 0)
333                         break;
334
335                 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
336                 /* data  = next keyword */
337                 /* value = next value ie stuff after equal sign */
338
339                 next_keyword = strchr(data,','); /* BB handle sep= */
340         
341                 /* temporarily null terminate end of keyword=value pair */
342                 if(next_keyword)
343                         *next_keyword++ = 0;
344
345                 /* temporarily null terminate keyword to make keyword and value distinct */
346                 if ((value = strchr(data, '=')) != NULL) {
347                         *value = '\0';
348                         value++;
349                 }
350
351                 if (strncmp(data, "users",5) == 0) {
352                         if(!value || !*value) {
353                                 goto nocopy;
354                         }
355                 } else if (strncmp(data, "user_xattr",10) == 0) {
356                    /* do nothing - need to skip so not parsed as user name */
357                 } else if (strncmp(data, "user", 4) == 0) {
358
359                         if (!value || !*value) {
360                                 if(data[4] == '\0') {
361                                         if(verboseflag)
362                                                 printf("\nskipping empty user mount parameter\n");
363                                         /* remove the parm since it would otherwise be confusing
364                                         to the kernel code which would think it was a real username */
365                                         goto nocopy;
366                                 } else {
367                                         printf("username specified with no parameter\n");
368                                         return 1;       /* needs_arg; */
369                                 }
370                         } else {
371                                 if (strnlen(value, 260) < 260) {
372                                         got_user=1;
373                                         percent_char = strchr(value,'%');
374                                         if(percent_char) {
375                                                 *percent_char = ',';
376                                                 if(mountpassword == NULL)
377                                                         mountpassword = calloc(65,1);
378                                                 if(mountpassword) {
379                                                         if(got_password)
380                                                                 printf("\nmount.cifs warning - password specified twice\n");
381                                                         got_password = 1;
382                                                         percent_char++;
383                                                         strncpy(mountpassword, percent_char,64);
384                                                 /*  remove password from username */
385                                                         while(*percent_char != 0) {
386                                                                 *percent_char = ',';
387                                                                 percent_char++;
388                                                         }
389                                                 }
390                                         }
391                                         /* this is only case in which the user
392                                         name buf is not malloc - so we have to
393                                         check for domain name embedded within
394                                         the user name here since the later
395                                         call to check_for_domain will not be
396                                         invoked */
397                                         domain_name = check_for_domain(&value);
398                                 } else {
399                                         printf("username too long\n");
400                                         return 1;
401                                 }
402                         }
403                 } else if (strncmp(data, "pass", 4) == 0) {
404                         if (!value || !*value) {
405                                 if(got_password) {
406                                         printf("\npassword specified twice, ignoring second\n");
407                                 } else
408                                         got_password = 1;
409                         } else if (strnlen(value, 17) < 17) {
410                                 if(got_password)
411                                         printf("\nmount.cifs warning - password specified twice\n");
412                                 got_password = 1;
413                         } else {
414                                 printf("password too long\n");
415                                 return 1;
416                         }
417                 } else if (strncmp(data, "ip", 2) == 0) {
418                         if (!value || !*value) {
419                                 printf("target ip address argument missing");
420                         } else if (strnlen(value, 35) < 35) {
421                                 if(verboseflag)
422                                         printf("ip address %s override specified\n",value);
423                                 got_ip = 1;
424                         } else {
425                                 printf("ip address too long\n");
426                                 return 1;
427                         }
428                 } else if ((strncmp(data, "unc", 3) == 0)
429                    || (strncmp(data, "target", 6) == 0)
430                    || (strncmp(data, "path", 4) == 0)) {
431                         if (!value || !*value) {
432                                 printf("invalid path to network resource\n");
433                                 return 1;  /* needs_arg; */
434                         } else if(strnlen(value,5) < 5) {
435                                 printf("UNC name too short");
436                         }
437
438                         if (strnlen(value, 300) < 300) {
439                                 got_unc = 1;
440                                 if (strncmp(value, "//", 2) == 0) {
441                                         if(got_unc)
442                                                 printf("unc name specified twice, ignoring second\n");
443                                         else
444                                                 got_unc = 1;
445                                 } else if (strncmp(value, "\\\\", 2) != 0) {                       
446                                         printf("UNC Path does not begin with // or \\\\ \n");
447                                         return 1;
448                                 } else {
449                                         if(got_unc)
450                                                 printf("unc name specified twice, ignoring second\n");
451                                         else
452                                                 got_unc = 1;
453                                 }
454                         } else {
455                                 printf("CIFS: UNC name too long\n");
456                                 return 1;
457                         }
458                 } else if ((strncmp(data, "domain", 3) == 0)
459                            || (strncmp(data, "workgroup", 5) == 0)) {
460                         if (!value || !*value) {
461                                 printf("CIFS: invalid domain name\n");
462                                 return 1;       /* needs_arg; */
463                         }
464                         if (strnlen(value, 65) < 65) {
465                                 got_domain = 1;
466                         } else {
467                                 printf("domain name too long\n");
468                                 return 1;
469                         }
470                 } else if (strncmp(data, "cred", 4) == 0) {
471                         if (value && *value) {
472                                 rc = open_cred_file(value);
473                                 if(rc) {
474                                         printf("error %d opening credential file %s\n",rc, value);
475                                         return 1;
476                                 }
477                         } else {
478                                 printf("invalid credential file name specified\n");
479                                 return 1;
480                         }
481                 } else if (strncmp(data, "uid", 3) == 0) {
482                         if (value && *value) {
483                                 got_uid = 1;
484                                 if (!isdigit(*value)) {
485                                         struct passwd *pw;
486                                         static char temp[32];
487
488                                         if (!(pw = getpwnam(value))) {
489                                                 printf("bad user name \"%s\"\n", value);
490                                                 exit(1);
491                                         }
492                                         sprintf(temp, "%u", pw->pw_uid);
493                                         value = temp;
494                                         endpwent();
495                                 }
496                         }
497                 } else if (strncmp(data, "gid", 3) == 0) {
498                         if (value && *value) {
499                                 got_gid = 1;
500                                 if (!isdigit(*value)) {
501                                         struct group *gr;
502                                         static char temp[32];
503
504                                         if (!(gr = getgrnam(value))) {
505                                                 printf("bad group name \"%s\"\n", value);
506                                                 exit(1);
507                                         }
508                                         sprintf(temp, "%u", gr->gr_gid);
509                                         value = temp;
510                                         endpwent();
511                                 }
512                         }
513        /* fmask and dmask synonyms for people used to smbfs syntax */
514                 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
515                         if (!value || !*value) {
516                                 printf ("Option '%s' requires a numerical argument\n", data);
517                                 return 1;
518                         }
519
520                         if (value[0] != '0') {
521                                 printf ("WARNING: '%s' not expressed in octal.\n", data);
522                         }
523
524                         if (strcmp (data, "fmask") == 0) {
525                                 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
526                                 data = "file_mode"; /* BB fix this */
527                         }
528                 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
529                         if (!value || !*value) {
530                                 printf ("Option '%s' requires a numerical argument\n", data);
531                                 return 1;
532                         }
533
534                         if (value[0] != '0') {
535                                 printf ("WARNING: '%s' not expressed in octal.\n", data);
536                         }
537
538                         if (strcmp (data, "dmask") == 0) {
539                                 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
540                                 data = "dir_mode";
541                         }
542                         /* the following eight mount options should be
543                         stripped out from what is passed into the kernel
544                         since these eight options are best passed as the
545                         mount flags rather than redundantly to the kernel 
546                         and could generate spurious warnings depending on the
547                         level of the corresponding cifs vfs kernel code */
548                 } else if (strncmp(data, "nosuid", 6) == 0) {
549                         *filesys_flags |= MS_NOSUID;
550                 } else if (strncmp(data, "suid", 4) == 0) {
551                         *filesys_flags &= ~MS_NOSUID;
552                 } else if (strncmp(data, "nodev", 5) == 0) {
553                         *filesys_flags |= MS_NODEV;
554                 } else if ((strncmp(data, "nobrl", 5) == 0) || 
555                            (strncmp(data, "nolock", 6) == 0)) {
556                         *filesys_flags &= ~MS_MANDLOCK;
557                 } else if (strncmp(data, "dev", 3) == 0) {
558                         *filesys_flags &= ~MS_NODEV;
559                 } else if (strncmp(data, "noexec", 6) == 0) {
560                         *filesys_flags |= MS_NOEXEC;
561                 } else if (strncmp(data, "exec", 4) == 0) {
562                         *filesys_flags &= ~MS_NOEXEC;
563                 } else if (strncmp(data, "guest", 5) == 0) {
564                         got_password=1;
565                         /* remove the parm since it would otherwise be logged by kern */
566                         goto nocopy;
567                 } else if (strncmp(data, "ro", 2) == 0) {
568                         *filesys_flags |= MS_RDONLY;
569                 } else if (strncmp(data, "rw", 2) == 0) {
570                         *filesys_flags &= ~MS_RDONLY;
571                 } else if (strncmp(data, "remount", 7) == 0) {
572                         *filesys_flags |= MS_REMOUNT;
573                 } /* else if (strnicmp(data, "port", 4) == 0) {
574                         if (value && *value) {
575                                 vol->port =
576                                         simple_strtoul(value, &value, 0);
577                         }
578                 } else if (strnicmp(data, "rsize", 5) == 0) {
579                         if (value && *value) {
580                                 vol->rsize =
581                                         simple_strtoul(value, &value, 0);
582                         }
583                 } else if (strnicmp(data, "wsize", 5) == 0) {
584                         if (value && *value) {
585                                 vol->wsize =
586                                         simple_strtoul(value, &value, 0);
587                         }
588                 } else if (strnicmp(data, "version", 3) == 0) {
589                 } else {
590                         printf("CIFS: Unknown mount option %s\n",data);
591                 } */ /* nothing to do on those four mount options above.
592                         Just pass to kernel and ignore them here */
593
594                 /* Copy (possibly modified) option to out */
595                 word_len = strlen(data);
596                 if (value)
597                         word_len += 1 + strlen(value);
598
599                 out = realloc(out, out_len + word_len + 2);
600                 if (out == NULL) {
601                         perror("malloc");
602                         exit(1);
603                 }
604
605                 if (out_len)
606                         out[out_len++] = ',';
607                 if (value)
608                         sprintf(out + out_len, "%s=%s", data, value);
609                 else
610                         sprintf(out + out_len, "%s", data);
611                 out_len = strlen(out);
612
613 nocopy:
614                 data = next_keyword;
615         }
616         *optionsp = out;
617         return 0;
618 }
619
620 /* replace all (one or more) commas with double commas */
621 static void check_for_comma(char ** ppasswrd)
622 {
623         char *new_pass_buf;
624         char *pass;
625         int i,j;
626         int number_of_commas = 0;
627         int len;
628
629         if(ppasswrd == NULL)
630                 return;
631         else 
632                 (pass = *ppasswrd);
633
634         len = strlen(pass);
635
636         for(i=0;i<len;i++)  {
637                 if(pass[i] == ',')
638                         number_of_commas++;
639         }
640
641         if(number_of_commas == 0)
642                 return;
643         if(number_of_commas > 64) {
644                 /* would otherwise overflow the mount options buffer */
645                 printf("\nInvalid password. Password contains too many commas.\n");
646                 return;
647         }
648
649         new_pass_buf = malloc(len+number_of_commas+1);
650         if(new_pass_buf == NULL)
651                 return;
652
653         for(i=0,j=0;i<len;i++,j++) {
654                 new_pass_buf[j] = pass[i];
655                 if(pass[i] == ',') {
656                         j++;
657                         new_pass_buf[j] = pass[i];
658                 }
659         }
660         new_pass_buf[len+number_of_commas] = 0;
661
662         free(*ppasswrd);
663         *ppasswrd = new_pass_buf;
664         
665         return;
666 }
667
668 /* Usernames can not have backslash in them and we use
669    [BB check if usernames can have forward slash in them BB] 
670    backslash as domain\user separator character
671 */
672 static char * check_for_domain(char **ppuser)
673 {
674         char * original_string;
675         char * usernm;
676         char * domainnm;
677         int    original_len;
678         int    len;
679         int    i;
680
681         if(ppuser == NULL)
682                 return NULL;
683
684         original_string = *ppuser;
685
686         if (original_string == NULL)
687                 return NULL;
688         
689         original_len = strlen(original_string);
690
691         usernm = strchr(*ppuser,'/');
692         if (usernm == NULL) {
693                 usernm = strchr(*ppuser,'\\');
694                 if (usernm == NULL)
695                         return NULL;
696         }
697
698         if(got_domain) {
699                 printf("Domain name specified twice. Username probably malformed\n");
700                 return NULL;
701         }
702
703         usernm[0] = 0;
704         domainnm = *ppuser;
705         if (domainnm[0] != 0) {
706                 got_domain = 1;
707         } else {
708                 printf("null domain\n");
709         }
710         len = strlen(domainnm);
711         /* reset domainm to new buffer, and copy
712         domain name into it */
713         domainnm = malloc(len+1);
714         if(domainnm == NULL)
715                 return NULL;
716
717         strcpy(domainnm,*ppuser);
718
719 /*      move_string(*ppuser, usernm+1) */
720         len = strlen(usernm+1);
721
722         if(len >= original_len) {
723                 /* should not happen */
724                 return domainnm;
725         }
726
727         for(i=0;i<original_len;i++) {
728                 if(i<len)
729                         original_string[i] = usernm[i+1];
730                 else /* stuff with commas to remove last parm */
731                         original_string[i] = ',';
732         }
733
734         /* BB add check for more than one slash? 
735           strchr(*ppuser,'/');
736           strchr(*ppuser,'\\') 
737         */
738         
739         return domainnm;
740 }
741
742 /* Note that caller frees the returned buffer if necessary */
743 static char * parse_server(char ** punc_name)
744 {
745         char * unc_name = *punc_name;
746         int length = strnlen(unc_name,1024);
747         char * share;
748         char * ipaddress_string = NULL;
749         struct hostent * host_entry = NULL;
750         struct in_addr server_ipaddr;
751
752         if(length > 1023) {
753                 printf("mount error: UNC name too long");
754                 return NULL;
755         }
756         if (strncasecmp("cifs://",unc_name,7) == 0)
757                 return parse_cifs_url(unc_name+7);
758         if (strncasecmp("smb://",unc_name,6) == 0) {
759                 return parse_cifs_url(unc_name+6);
760         }
761
762         if(length < 3) {
763                 /* BB add code to find DFS root here */
764                 printf("\nMounting the DFS root for domain not implemented yet\n");
765                 return NULL;
766         } else {
767                 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
768                         /* check for nfs syntax ie server:share */
769                         share = strchr(unc_name,':');
770                         if(share) {
771                                 free_share_name = 1;
772                                 *punc_name = malloc(length+3);
773                                 if(*punc_name == NULL) {
774                                         /* put the original string back  if 
775                                            no memory left */
776                                         *punc_name = unc_name;
777                                         return NULL;
778                                 }
779                                         
780                                 *share = '/';
781                                 strncpy((*punc_name)+2,unc_name,length);
782                                 unc_name = *punc_name;
783                                 unc_name[length+2] = 0;
784                                 goto continue_unc_parsing;
785                         } else {
786                                 printf("mount error: improperly formatted UNC name.");
787                                 printf(" %s does not begin with \\\\ or //\n",unc_name);
788                                 return NULL;
789                         }
790                 } else {
791 continue_unc_parsing:
792                         unc_name[0] = '/';
793                         unc_name[1] = '/';
794                         unc_name += 2;
795                         if ((share = strchr(unc_name, '/')) || 
796                                 (share = strchr(unc_name,'\\'))) {
797                                 *share = 0;  /* temporarily terminate the string */
798                                 share += 1;
799                                 if(got_ip == 0) {
800                                         host_entry = gethostbyname(unc_name);
801                                 }
802                                 *(share - 1) = '/'; /* put the slash back */
803                                 if(got_ip) {
804                                         if(verboseflag)
805                                                 printf("ip address specified explicitly\n");
806                                         return NULL;
807                                 }
808                                 if(host_entry == NULL) {
809                                         printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
810                                         return NULL;
811                                 } else {
812                                         /* BB should we pass an alternate version of the share name as Unicode */
813                                         /* BB what about ipv6? BB */
814                                         /* BB add retries with alternate servers in list */
815
816                                         memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
817
818                                         ipaddress_string = inet_ntoa(server_ipaddr);                                                                                     
819                                         if(ipaddress_string == NULL) {
820                                                 printf("mount error: could not get valid ip address for target server\n");
821                                                 return NULL;
822                                         }
823                                         return ipaddress_string; 
824                                 }
825                         } else {
826                                 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
827                                 printf("Mounting the DFS root for a particular server not implemented yet\n");
828                                 return NULL;
829                         }
830                 }
831         }
832 }
833
834 static struct option longopts[] = {
835         { "all", 0, NULL, 'a' },
836         { "help",0, NULL, 'h' },
837         { "move",0, NULL, 'm' },
838         { "bind",0, NULL, 'b' },
839         { "read-only", 0, NULL, 'r' },
840         { "ro", 0, NULL, 'r' },
841         { "verbose", 0, NULL, 'v' },
842         { "version", 0, NULL, 'V' },
843         { "read-write", 0, NULL, 'w' },
844         { "rw", 0, NULL, 'w' },
845         { "options", 1, NULL, 'o' },
846         { "type", 1, NULL, 't' },
847         { "rsize",1, NULL, 'R' },
848         { "wsize",1, NULL, 'W' },
849         { "uid", 1, NULL, '1'},
850         { "gid", 1, NULL, '2'},
851         { "user",1,NULL,'u'},
852         { "username",1,NULL,'u'},
853         { "dom",1,NULL,'d'},
854         { "domain",1,NULL,'d'},
855         { "password",1,NULL,'p'},
856         { "pass",1,NULL,'p'},
857         { "credentials",1,NULL,'c'},
858         { "port",1,NULL,'P'},
859         /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
860         { NULL, 0, NULL, 0 }
861 };
862
863 int main(int argc, char ** argv)
864 {
865         int c;
866         int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
867         char * orgoptions = NULL;
868         char * share_name = NULL;
869         char * ipaddr = NULL;
870         char * uuid = NULL;
871         char * mountpoint = NULL;
872         char * options;
873         char * resolved_path;
874         char * temp;
875         int rc;
876         int rsize = 0;
877         int wsize = 0;
878         int nomtab = 0;
879         int uid = 0;
880         int gid = 0;
881         int optlen = 0;
882         int orgoptlen = 0;
883         int retry = 0; /* set when we have to retry mount with uppercase */
884         struct stat statbuf;
885         struct utsname sysinfo;
886         struct mntent mountent;
887         FILE * pmntfile;
888
889         /* setlocale(LC_ALL, "");
890         bindtextdomain(PACKAGE, LOCALEDIR);
891         textdomain(PACKAGE); */
892
893         if(argc && argv) {
894                 thisprogram = argv[0];
895         } else {
896                 mount_cifs_usage();
897                 exit(1);
898         }
899
900         if(thisprogram == NULL)
901                 thisprogram = "mount.cifs";
902
903         uname(&sysinfo);
904         /* BB add workstation name and domain and pass down */
905
906 /* #ifdef _GNU_SOURCE
907         printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
908 #endif */
909         if(argc > 2) {
910                 share_name = argv[1];
911                 mountpoint = argv[2];
912         }
913
914         /* add sharename in opts string as unc= parm */
915
916         while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
917                          longopts, NULL)) != -1) {
918                 switch (c) {
919 /* No code to do the following  options yet */
920 /*      case 'l':
921                 list_with_volumelabel = 1;
922                 break;
923         case 'L':
924                 volumelabel = optarg;
925                 break; */
926 /*      case 'a':              
927                 ++mount_all;
928                 break; */
929
930                 case '?':
931                 case 'h':        /* help */
932                         mount_cifs_usage ();
933                         exit(1);
934                 case 'n':
935                     ++nomtab;
936                     break;
937                 case 'b':
938                         flags |= MS_BIND;
939                         break;
940                 case 'm':
941                         flags |= MS_MOVE;
942                         break;
943                 case 'o':
944                         orgoptions = strdup(optarg);
945                     break;
946                 case 'r':  /* mount readonly */
947                         flags |= MS_RDONLY;
948                         break;
949                 case 'U':
950                         uuid = optarg;
951                         break;
952                 case 'v':
953                         ++verboseflag;
954                         break;
955                 case 'V':          
956                         printf ("mount.cifs version: %s.%s%s\n",
957                         MOUNT_CIFS_VERSION_MAJOR,
958                         MOUNT_CIFS_VERSION_MINOR,
959                         MOUNT_CIFS_VENDOR_SUFFIX);
960                         if(mountpassword) {
961                                 memset(mountpassword,0,64);
962                         }
963                         exit (0);
964                 case 'w':
965                         flags &= ~MS_RDONLY;
966                         break;
967                 case 'R':
968                         rsize = atoi(optarg) ;
969                         break;
970                 case 'W':
971                         wsize = atoi(optarg);
972                         break;
973                 case '1':
974                         if (isdigit(*optarg)) {
975                                 char *ep;
976
977                                 uid = strtoul(optarg, &ep, 10);
978                                 if (*ep) {
979                                         printf("bad uid value \"%s\"\n", optarg);
980                                         exit(1);
981                                 }
982                         } else {
983                                 struct passwd *pw;
984
985                                 if (!(pw = getpwnam(optarg))) {
986                                         printf("bad user name \"%s\"\n", optarg);
987                                         exit(1);
988                                 }
989                                 uid = pw->pw_uid;
990                                 endpwent();
991                         }
992                         break;
993                 case '2':
994                         if (isdigit(*optarg)) {
995                                 char *ep;
996
997                                 gid = strtoul(optarg, &ep, 10);
998                                 if (*ep) {
999                                         printf("bad gid value \"%s\"\n", optarg);
1000                                         exit(1);
1001                                 }
1002                         } else {
1003                                 struct group *gr;
1004
1005                                 if (!(gr = getgrnam(optarg))) {
1006                                         printf("bad user name \"%s\"\n", optarg);
1007                                         exit(1);
1008                                 }
1009                                 gid = gr->gr_gid;
1010                                 endpwent();
1011                         }
1012                         break;
1013                 case 'u':
1014                         got_user = 1;
1015                         user_name = optarg;
1016                         break;
1017                 case 'd':
1018                         domain_name = optarg; /* BB fix this - currently ignored */
1019                         got_domain = 1;
1020                         break;
1021                 case 'p':
1022                         if(mountpassword == NULL)
1023                                 mountpassword = calloc(65,1);
1024                         if(mountpassword) {
1025                                 got_password = 1;
1026                                 strncpy(mountpassword,optarg,64);
1027                         }
1028                         break;
1029                 case 'S':
1030                         get_password_from_file(0 /* stdin */,NULL);
1031                         break;
1032                 case 't':
1033                         break;
1034                 default:
1035                         printf("unknown mount option %c\n",c);
1036                         mount_cifs_usage();
1037                         exit(1);
1038                 }
1039         }
1040
1041         if((argc < 3) || (share_name == NULL) || (mountpoint == NULL)) {
1042                 mount_cifs_usage();
1043                 exit(1);
1044         }
1045
1046         if (getenv("PASSWD")) {
1047                 if(mountpassword == NULL)
1048                         mountpassword = calloc(65,1);
1049                 if(mountpassword) {
1050                         strncpy(mountpassword,getenv("PASSWD"),64);
1051                         got_password = 1;
1052                 }
1053         } else if (getenv("PASSWD_FD")) {
1054                 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1055         } else if (getenv("PASSWD_FILE")) {
1056                 get_password_from_file(0, getenv("PASSWD_FILE"));
1057         }
1058
1059         if (orgoptions && parse_options(&orgoptions, &flags))
1060                 return -1;
1061         ipaddr = parse_server(&share_name);
1062         if((ipaddr == NULL) && (got_ip == 0)) {
1063                 printf("No ip address specified and hostname not found\n");
1064                 return -1;
1065         }
1066         
1067         /* BB save off path and pop after mount returns? */
1068         resolved_path = malloc(PATH_MAX+1);
1069         if(resolved_path) {
1070                 /* Note that if we can not canonicalize the name, we get
1071                 another chance to see if it is valid when we chdir to it */
1072                 if (realpath(mountpoint, resolved_path)) {
1073                         mountpoint = resolved_path; 
1074                 }
1075         }
1076         if(chdir(mountpoint)) {
1077                 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1078                 return -1;
1079         }
1080
1081         if(stat (".", &statbuf)) {
1082                 printf("mount error: mount point %s does not exist\n",mountpoint);
1083                 return -1;
1084         }
1085
1086         if (S_ISDIR(statbuf.st_mode) == 0) {
1087                 printf("mount error: mount point %s is not a directory\n",mountpoint);
1088                 return -1;
1089         }
1090
1091         if((getuid() != 0) && (geteuid() == 0)) {
1092                 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1093 #ifndef CIFS_ALLOW_USR_SUID
1094                         /* Do not allow user mounts to control suid flag
1095                         for mount unless explicitly built that way */
1096                         flags |= MS_NOSUID | MS_NODEV;
1097 #endif                                          
1098                 } else {
1099                         printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n"); 
1100                         return -1;
1101                 }
1102         }
1103
1104         if(got_user == 0) {
1105                 user_name = getusername();
1106                 got_user = 1;
1107         }
1108        
1109         if(got_password == 0) {
1110                 mountpassword = getpass("Password: "); /* BB obsolete */
1111                 got_password = 1;
1112         }
1113         /* FIXME launch daemon (handles dfs name resolution and credential change) 
1114            remember to clear parms and overwrite password field before launching */
1115 mount_retry:
1116         if(orgoptions) {
1117                 optlen = strlen(orgoptions);
1118                 orgoptlen = optlen;
1119         } else
1120                 optlen = 0;
1121         if(share_name)
1122                 optlen += strlen(share_name) + 4;
1123         else {
1124                 printf("No server share name specified\n");
1125                 printf("\nMounting the DFS root for server not implemented yet\n");
1126                 exit(1);
1127         }
1128         if(user_name)
1129                 optlen += strlen(user_name) + 6;
1130         if(ipaddr)
1131                 optlen += strlen(ipaddr) + 4;
1132         if(mountpassword)
1133                 optlen += strlen(mountpassword) + 6;
1134         options = malloc(optlen + 10 + 64 /* space for commas in password */ + 8 /* space for domain=  , domain name itself was counted as part of the length username string above */);
1135
1136         if(options == NULL) {
1137                 printf("Could not allocate memory for mount options\n");
1138                 return -1;
1139         }
1140                 
1141
1142         options[0] = 0;
1143         strncat(options,"unc=",4);
1144         strcat(options,share_name);
1145         /* scan backwards and reverse direction of slash */
1146         temp = strrchr(options, '/');
1147         if(temp > options + 6)
1148                 *temp = '\\';
1149         if(ipaddr) {
1150                 strncat(options,",ip=",4);
1151                 strcat(options,ipaddr);
1152         }
1153
1154         if(user_name) {
1155                 /* check for syntax like user=domain\user */
1156                 if(got_domain == 0)
1157                         domain_name = check_for_domain(&user_name);
1158                 strncat(options,",user=",6);
1159                 strcat(options,user_name);
1160         }
1161         if(retry == 0) {
1162                 if(domain_name) { 
1163                         /* extra length accounted for in option string above */
1164                         strncat(options,",domain=",8);
1165                         strcat(options,domain_name);
1166                 }
1167         }
1168         if(mountpassword) {
1169                 /* Commas have to be doubled, or else they will
1170                 look like the parameter separator */
1171 /*              if(sep is not set)*/
1172                 if(retry == 0)
1173                         check_for_comma(&mountpassword);
1174                 strncat(options,",pass=",6);
1175                 strcat(options,mountpassword);
1176         }
1177
1178         strncat(options,",ver=",5);
1179         strcat(options,MOUNT_CIFS_VERSION_MAJOR);
1180
1181         if(orgoptions) {
1182                 strcat(options,",");
1183                 strcat(options,orgoptions);
1184         }
1185         if(verboseflag)
1186                 printf("\nmount.cifs kernel mount options %s \n",options);
1187         if(mount(share_name, mountpoint, "cifs", flags, options)) {
1188         /* remember to kill daemon on error */
1189                 char * tmp;
1190
1191                 switch (errno) {
1192                 case 0:
1193                         printf("mount failed but no error number set\n");
1194                         break;
1195                 case ENODEV:
1196                         printf("mount error: cifs filesystem not supported by the system\n");
1197                         break;
1198                 case ENXIO:
1199                         if(retry == 0) {
1200                                 retry = 1;
1201                                 tmp = share_name;
1202                                 while (*tmp && !(((unsigned char)tmp[0]) & 0x80)) {
1203                                         *tmp = toupper((unsigned char)*tmp);
1204                                         tmp++;
1205                                 }
1206                                 if(!*tmp) {
1207                                         printf("retrying with upper case share name\n");
1208                                         goto mount_retry;
1209                                 }
1210                         }
1211                 default:
1212                         
1213                         printf("mount error %d = %s\n",errno,strerror(errno));
1214                 }
1215                 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1216                 if(mountpassword) {
1217                         memset(mountpassword,0,64);
1218                 }
1219                 return -1;
1220         } else {
1221                 pmntfile = setmntent(MOUNTED, "a+");
1222                 if(pmntfile) {
1223                         mountent.mnt_fsname = share_name;
1224                         mountent.mnt_dir = mountpoint; 
1225                         mountent.mnt_type = CONST_DISCARD(char *,"cifs"); 
1226                         mountent.mnt_opts = malloc(220);
1227                         if(mountent.mnt_opts) {
1228                                 char * mount_user = getusername();
1229                                 memset(mountent.mnt_opts,0,200);
1230                                 if(flags & MS_RDONLY)
1231                                         strcat(mountent.mnt_opts,"ro");
1232                                 else
1233                                         strcat(mountent.mnt_opts,"rw");
1234                                 if(flags & MS_MANDLOCK)
1235                                         strcat(mountent.mnt_opts,",mand");
1236                                 if(flags & MS_NOEXEC)
1237                                         strcat(mountent.mnt_opts,",noexec");
1238                                 if(flags & MS_NOSUID)
1239                                         strcat(mountent.mnt_opts,",nosuid");
1240                                 if(flags & MS_NODEV)
1241                                         strcat(mountent.mnt_opts,",nodev");
1242                                 if(flags & MS_SYNCHRONOUS)
1243                                         strcat(mountent.mnt_opts,",synch");
1244                                 if(mount_user) {
1245                                         if(getuid() != 0) {
1246                                                 strcat(mountent.mnt_opts,",user=");
1247                                                 strcat(mountent.mnt_opts,mount_user);
1248                                         }
1249                                         free(mount_user);
1250                                 }
1251                         }
1252                         mountent.mnt_freq = 0;
1253                         mountent.mnt_passno = 0;
1254                         rc = addmntent(pmntfile,&mountent);
1255                         endmntent(pmntfile);
1256                         if(mountent.mnt_opts)
1257                                 free(mountent.mnt_opts);
1258                 } else {
1259                     printf("could not update mount table\n");
1260                 }
1261         }
1262         if(mountpassword) {
1263                 int len = strlen(mountpassword);
1264                 memset(mountpassword,0,len);
1265                 free(mountpassword);
1266         }
1267
1268         if(options) {
1269                 memset(options,0,optlen);
1270                 free(options);
1271         }
1272
1273         if(orgoptions) {
1274                 memset(orgoptions,0,orgoptlen);
1275                 free(orgoptions);
1276         }
1277         if(resolved_path) {
1278                 free(resolved_path);
1279         }
1280
1281         if(free_share_name) {
1282                 free(share_name);
1283                 }
1284         return 0;
1285 }
1286