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