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