r22982: Do not prompt for password on sec=none for mount.cifs (and allow guest
[ira/wip.git] / source3 / 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, "sec", 3) == 0) {
423                         if (value) {
424                                 if (!strcmp(value, "none"))
425                                         got_password = 1;
426                         }
427                 } else if (strncmp(data, "ip", 2) == 0) {
428                         if (!value || !*value) {
429                                 printf("target ip address argument missing");
430                         } else if (strnlen(value, 35) < 35) {
431                                 if(verboseflag)
432                                         printf("ip address %s override specified\n",value);
433                                 got_ip = 1;
434                         } else {
435                                 printf("ip address too long\n");
436                                 return 1;
437                         }
438                 } else if ((strncmp(data, "unc", 3) == 0)
439                    || (strncmp(data, "target", 6) == 0)
440                    || (strncmp(data, "path", 4) == 0)) {
441                         if (!value || !*value) {
442                                 printf("invalid path to network resource\n");
443                                 return 1;  /* needs_arg; */
444                         } else if(strnlen(value,5) < 5) {
445                                 printf("UNC name too short");
446                         }
447
448                         if (strnlen(value, 300) < 300) {
449                                 got_unc = 1;
450                                 if (strncmp(value, "//", 2) == 0) {
451                                         if(got_unc)
452                                                 printf("unc name specified twice, ignoring second\n");
453                                         else
454                                                 got_unc = 1;
455                                 } else if (strncmp(value, "\\\\", 2) != 0) {                       
456                                         printf("UNC Path does not begin with // or \\\\ \n");
457                                         return 1;
458                                 } else {
459                                         if(got_unc)
460                                                 printf("unc name specified twice, ignoring second\n");
461                                         else
462                                                 got_unc = 1;
463                                 }
464                         } else {
465                                 printf("CIFS: UNC name too long\n");
466                                 return 1;
467                         }
468                 } else if ((strncmp(data, "domain", 3) == 0)
469                            || (strncmp(data, "workgroup", 5) == 0)) {
470                         if (!value || !*value) {
471                                 printf("CIFS: invalid domain name\n");
472                                 return 1;       /* needs_arg; */
473                         }
474                         if (strnlen(value, 65) < 65) {
475                                 got_domain = 1;
476                         } else {
477                                 printf("domain name too long\n");
478                                 return 1;
479                         }
480                 } else if (strncmp(data, "cred", 4) == 0) {
481                         if (value && *value) {
482                                 rc = open_cred_file(value);
483                                 if(rc) {
484                                         printf("error %d opening credential file %s\n",rc, value);
485                                         return 1;
486                                 }
487                         } else {
488                                 printf("invalid credential file name specified\n");
489                                 return 1;
490                         }
491                 } else if (strncmp(data, "uid", 3) == 0) {
492                         if (value && *value) {
493                                 got_uid = 1;
494                                 if (!isdigit(*value)) {
495                                         struct passwd *pw;
496                                         static char temp[32];
497
498                                         if (!(pw = getpwnam(value))) {
499                                                 printf("bad user name \"%s\"\n", value);
500                                                 exit(1);
501                                         }
502                                         sprintf(temp, "%u", pw->pw_uid);
503                                         value = temp;
504                                         endpwent();
505                                 }
506                         }
507                 } else if (strncmp(data, "gid", 3) == 0) {
508                         if (value && *value) {
509                                 got_gid = 1;
510                                 if (!isdigit(*value)) {
511                                         struct group *gr;
512                                         static char temp[32];
513
514                                         if (!(gr = getgrnam(value))) {
515                                                 printf("bad group name \"%s\"\n", value);
516                                                 exit(1);
517                                         }
518                                         sprintf(temp, "%u", gr->gr_gid);
519                                         value = temp;
520                                         endpwent();
521                                 }
522                         }
523        /* fmask and dmask synonyms for people used to smbfs syntax */
524                 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
525                         if (!value || !*value) {
526                                 printf ("Option '%s' requires a numerical argument\n", data);
527                                 return 1;
528                         }
529
530                         if (value[0] != '0') {
531                                 printf ("WARNING: '%s' not expressed in octal.\n", data);
532                         }
533
534                         if (strcmp (data, "fmask") == 0) {
535                                 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
536                                 data = "file_mode"; /* BB fix this */
537                         }
538                 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
539                         if (!value || !*value) {
540                                 printf ("Option '%s' requires a numerical argument\n", data);
541                                 return 1;
542                         }
543
544                         if (value[0] != '0') {
545                                 printf ("WARNING: '%s' not expressed in octal.\n", data);
546                         }
547
548                         if (strcmp (data, "dmask") == 0) {
549                                 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
550                                 data = "dir_mode";
551                         }
552                         /* the following eight mount options should be
553                         stripped out from what is passed into the kernel
554                         since these eight options are best passed as the
555                         mount flags rather than redundantly to the kernel 
556                         and could generate spurious warnings depending on the
557                         level of the corresponding cifs vfs kernel code */
558                 } else if (strncmp(data, "nosuid", 6) == 0) {
559                         *filesys_flags |= MS_NOSUID;
560                 } else if (strncmp(data, "suid", 4) == 0) {
561                         *filesys_flags &= ~MS_NOSUID;
562                 } else if (strncmp(data, "nodev", 5) == 0) {
563                         *filesys_flags |= MS_NODEV;
564                 } else if ((strncmp(data, "nobrl", 5) == 0) || 
565                            (strncmp(data, "nolock", 6) == 0)) {
566                         *filesys_flags &= ~MS_MANDLOCK;
567                 } else if (strncmp(data, "dev", 3) == 0) {
568                         *filesys_flags &= ~MS_NODEV;
569                 } else if (strncmp(data, "noexec", 6) == 0) {
570                         *filesys_flags |= MS_NOEXEC;
571                 } else if (strncmp(data, "exec", 4) == 0) {
572                         *filesys_flags &= ~MS_NOEXEC;
573                 } else if (strncmp(data, "guest", 5) == 0) {
574                         got_password=1;
575                 } else if (strncmp(data, "ro", 2) == 0) {
576                         *filesys_flags |= MS_RDONLY;
577                 } else if (strncmp(data, "rw", 2) == 0) {
578                         *filesys_flags &= ~MS_RDONLY;
579                 } else if (strncmp(data, "remount", 7) == 0) {
580                         *filesys_flags |= MS_REMOUNT;
581                 } /* else if (strnicmp(data, "port", 4) == 0) {
582                         if (value && *value) {
583                                 vol->port =
584                                         simple_strtoul(value, &value, 0);
585                         }
586                 } else if (strnicmp(data, "rsize", 5) == 0) {
587                         if (value && *value) {
588                                 vol->rsize =
589                                         simple_strtoul(value, &value, 0);
590                         }
591                 } else if (strnicmp(data, "wsize", 5) == 0) {
592                         if (value && *value) {
593                                 vol->wsize =
594                                         simple_strtoul(value, &value, 0);
595                         }
596                 } else if (strnicmp(data, "version", 3) == 0) {
597                 } else {
598                         printf("CIFS: Unknown mount option %s\n",data);
599                 } */ /* nothing to do on those four mount options above.
600                         Just pass to kernel and ignore them here */
601
602                 /* Copy (possibly modified) option to out */
603                 word_len = strlen(data);
604                 if (value)
605                         word_len += 1 + strlen(value);
606
607                 out = (char *)realloc(out, out_len + word_len + 2);
608                 if (out == NULL) {
609                         perror("malloc");
610                         exit(1);
611                 }
612
613                 if (out_len)
614                         out[out_len++] = ',';
615                 if (value)
616                         sprintf(out + out_len, "%s=%s", data, value);
617                 else
618                         sprintf(out + out_len, "%s", data);
619                 out_len = strlen(out);
620
621 nocopy:
622                 data = next_keyword;
623         }
624         free(*optionsp);
625         *optionsp = out;
626         return 0;
627 }
628
629 /* replace all (one or more) commas with double commas */
630 static void check_for_comma(char ** ppasswrd)
631 {
632         char *new_pass_buf;
633         char *pass;
634         int i,j;
635         int number_of_commas = 0;
636         int len;
637
638         if(ppasswrd == NULL)
639                 return;
640         else 
641                 (pass = *ppasswrd);
642
643         len = strlen(pass);
644
645         for(i=0;i<len;i++)  {
646                 if(pass[i] == ',')
647                         number_of_commas++;
648         }
649
650         if(number_of_commas == 0)
651                 return;
652         if(number_of_commas > 64) {
653                 /* would otherwise overflow the mount options buffer */
654                 printf("\nInvalid password. Password contains too many commas.\n");
655                 return;
656         }
657
658         new_pass_buf = (char *)malloc(len+number_of_commas+1);
659         if(new_pass_buf == NULL)
660                 return;
661
662         for(i=0,j=0;i<len;i++,j++) {
663                 new_pass_buf[j] = pass[i];
664                 if(pass[i] == ',') {
665                         j++;
666                         new_pass_buf[j] = pass[i];
667                 }
668         }
669         new_pass_buf[len+number_of_commas] = 0;
670
671         free(*ppasswrd);
672         *ppasswrd = new_pass_buf;
673         
674         return;
675 }
676
677 /* Usernames can not have backslash in them and we use
678    [BB check if usernames can have forward slash in them BB] 
679    backslash as domain\user separator character
680 */
681 static char * check_for_domain(char **ppuser)
682 {
683         char * original_string;
684         char * usernm;
685         char * domainnm;
686         int    original_len;
687         int    len;
688         int    i;
689
690         if(ppuser == NULL)
691                 return NULL;
692
693         original_string = *ppuser;
694
695         if (original_string == NULL)
696                 return NULL;
697         
698         original_len = strlen(original_string);
699
700         usernm = strchr(*ppuser,'/');
701         if (usernm == NULL) {
702                 usernm = strchr(*ppuser,'\\');
703                 if (usernm == NULL)
704                         return NULL;
705         }
706
707         if(got_domain) {
708                 printf("Domain name specified twice. Username probably malformed\n");
709                 return NULL;
710         }
711
712         usernm[0] = 0;
713         domainnm = *ppuser;
714         if (domainnm[0] != 0) {
715                 got_domain = 1;
716         } else {
717                 printf("null domain\n");
718         }
719         len = strlen(domainnm);
720         /* reset domainm to new buffer, and copy
721         domain name into it */
722         domainnm = (char *)malloc(len+1);
723         if(domainnm == NULL)
724                 return NULL;
725
726         strcpy(domainnm,*ppuser);
727
728 /*      move_string(*ppuser, usernm+1) */
729         len = strlen(usernm+1);
730
731         if(len >= original_len) {
732                 /* should not happen */
733                 return domainnm;
734         }
735
736         for(i=0;i<original_len;i++) {
737                 if(i<len)
738                         original_string[i] = usernm[i+1];
739                 else /* stuff with commas to remove last parm */
740                         original_string[i] = ',';
741         }
742
743         /* BB add check for more than one slash? 
744           strchr(*ppuser,'/');
745           strchr(*ppuser,'\\') 
746         */
747         
748         return domainnm;
749 }
750
751 /* Note that caller frees the returned buffer if necessary */
752 static char * parse_server(char ** punc_name)
753 {
754         char * unc_name = *punc_name;
755         int length = strnlen(unc_name,1024);
756         char * share;
757         char * ipaddress_string = NULL;
758         struct hostent * host_entry = NULL;
759         struct in_addr server_ipaddr;
760
761         if(length > 1023) {
762                 printf("mount error: UNC name too long");
763                 return NULL;
764         }
765         if (strncasecmp("cifs://",unc_name,7) == 0)
766                 return parse_cifs_url(unc_name+7);
767         if (strncasecmp("smb://",unc_name,6) == 0) {
768                 return parse_cifs_url(unc_name+6);
769         }
770
771         if(length < 3) {
772                 /* BB add code to find DFS root here */
773                 printf("\nMounting the DFS root for domain not implemented yet\n");
774                 return NULL;
775         } else {
776                 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
777                         /* check for nfs syntax ie server:share */
778                         share = strchr(unc_name,':');
779                         if(share) {
780                                 free_share_name = 1;
781                                 *punc_name = (char *)malloc(length+3);
782                                 if(*punc_name == NULL) {
783                                         /* put the original string back  if 
784                                            no memory left */
785                                         *punc_name = unc_name;
786                                         return NULL;
787                                 }
788                                         
789                                 *share = '/';
790                                 strncpy((*punc_name)+2,unc_name,length);
791                                 unc_name = *punc_name;
792                                 unc_name[length+2] = 0;
793                                 goto continue_unc_parsing;
794                         } else {
795                                 printf("mount error: improperly formatted UNC name.");
796                                 printf(" %s does not begin with \\\\ or //\n",unc_name);
797                                 return NULL;
798                         }
799                 } else {
800 continue_unc_parsing:
801                         unc_name[0] = '/';
802                         unc_name[1] = '/';
803                         unc_name += 2;
804                         if ((share = strchr(unc_name, '/')) || 
805                                 (share = strchr(unc_name,'\\'))) {
806                                 *share = 0;  /* temporarily terminate the string */
807                                 share += 1;
808                                 if(got_ip == 0) {
809                                         host_entry = gethostbyname(unc_name);
810                                 }
811                                 *(share - 1) = '/'; /* put the slash back */
812                                 if ((prefixpath = strchr(share, '/'))) {
813                                         *prefixpath = 0;  /* permanently terminate the string */
814                                         if (!strlen(++prefixpath))
815                                                 prefixpath = NULL; /* this needs to be done explicitly */
816                                 }
817                                 if(got_ip) {
818                                         if(verboseflag)
819                                                 printf("ip address specified explicitly\n");
820                                         return NULL;
821                                 }
822                                 if(host_entry == NULL) {
823                                         printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
824                                         return NULL;
825                                 } else {
826                                         /* BB should we pass an alternate version of the share name as Unicode */
827                                         /* BB what about ipv6? BB */
828                                         /* BB add retries with alternate servers in list */
829
830                                         memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
831
832                                         ipaddress_string = inet_ntoa(server_ipaddr);                                                                                     
833                                         if(ipaddress_string == NULL) {
834                                                 printf("mount error: could not get valid ip address for target server\n");
835                                                 return NULL;
836                                         }
837                                         return ipaddress_string; 
838                                 }
839                         } else {
840                                 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
841                                 printf("Mounting the DFS root for a particular server not implemented yet\n");
842                                 return NULL;
843                         }
844                 }
845         }
846 }
847
848 static struct option longopts[] = {
849         { "all", 0, NULL, 'a' },
850         { "help",0, NULL, 'h' },
851         { "move",0, NULL, 'm' },
852         { "bind",0, NULL, 'b' },
853         { "read-only", 0, NULL, 'r' },
854         { "ro", 0, NULL, 'r' },
855         { "verbose", 0, NULL, 'v' },
856         { "version", 0, NULL, 'V' },
857         { "read-write", 0, NULL, 'w' },
858         { "rw", 0, NULL, 'w' },
859         { "options", 1, NULL, 'o' },
860         { "type", 1, NULL, 't' },
861         { "rsize",1, NULL, 'R' },
862         { "wsize",1, NULL, 'W' },
863         { "uid", 1, NULL, '1'},
864         { "gid", 1, NULL, '2'},
865         { "user",1,NULL,'u'},
866         { "username",1,NULL,'u'},
867         { "dom",1,NULL,'d'},
868         { "domain",1,NULL,'d'},
869         { "password",1,NULL,'p'},
870         { "pass",1,NULL,'p'},
871         { "credentials",1,NULL,'c'},
872         { "port",1,NULL,'P'},
873         /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
874         { NULL, 0, NULL, 0 }
875 };
876
877 int main(int argc, char ** argv)
878 {
879         int c;
880         int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
881         char * orgoptions = NULL;
882         char * share_name = NULL;
883         char * ipaddr = NULL;
884         char * uuid = NULL;
885         char * mountpoint = NULL;
886         char * options = NULL;
887         char * resolved_path = NULL;
888         char * temp;
889         int rc;
890         int rsize = 0;
891         int wsize = 0;
892         int nomtab = 0;
893         int uid = 0;
894         int gid = 0;
895         int optlen = 0;
896         int orgoptlen = 0;
897         int retry = 0; /* set when we have to retry mount with uppercase */
898         struct stat statbuf;
899         struct utsname sysinfo;
900         struct mntent mountent;
901         FILE * pmntfile;
902
903         /* setlocale(LC_ALL, "");
904         bindtextdomain(PACKAGE, LOCALEDIR);
905         textdomain(PACKAGE); */
906
907         if(argc && argv) {
908                 thisprogram = argv[0];
909         } else {
910                 mount_cifs_usage();
911                 exit(1);
912         }
913
914         if(thisprogram == NULL)
915                 thisprogram = "mount.cifs";
916
917         uname(&sysinfo);
918         /* BB add workstation name and domain and pass down */
919
920 /* #ifdef _GNU_SOURCE
921         printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
922 #endif */
923         if(argc > 2) {
924                 share_name = argv[1];
925                 mountpoint = argv[2];
926         }
927
928         /* add sharename in opts string as unc= parm */
929
930         while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
931                          longopts, NULL)) != -1) {
932                 switch (c) {
933 /* No code to do the following  options yet */
934 /*      case 'l':
935                 list_with_volumelabel = 1;
936                 break;
937         case 'L':
938                 volumelabel = optarg;
939                 break; */
940 /*      case 'a':              
941                 ++mount_all;
942                 break; */
943
944                 case '?':
945                 case 'h':        /* help */
946                         mount_cifs_usage ();
947                         exit(1);
948                 case 'n':
949                     ++nomtab;
950                     break;
951                 case 'b':
952 #ifdef MS_BIND
953                         flags |= MS_BIND;
954 #else
955                         fprintf(stderr,
956                                 "option 'b' (MS_BIND) not supported\n");
957 #endif
958                         break;
959                 case 'm':
960 #ifdef MS_MOVE                
961                         flags |= MS_MOVE;
962 #else
963                         fprintf(stderr,
964                                 "option 'm' (MS_MOVE) not supported\n");
965 #endif
966                         break;
967                 case 'o':
968                         orgoptions = strdup(optarg);
969                     break;
970                 case 'r':  /* mount readonly */
971                         flags |= MS_RDONLY;
972                         break;
973                 case 'U':
974                         uuid = optarg;
975                         break;
976                 case 'v':
977                         ++verboseflag;
978                         break;
979                 case 'V':          
980                         printf ("mount.cifs version: %s.%s%s\n",
981                         MOUNT_CIFS_VERSION_MAJOR,
982                         MOUNT_CIFS_VERSION_MINOR,
983                         MOUNT_CIFS_VENDOR_SUFFIX);
984                         if(mountpassword) {
985                                 memset(mountpassword,0,64);
986                         }
987                         exit (0);
988                 case 'w':
989                         flags &= ~MS_RDONLY;
990                         break;
991                 case 'R':
992                         rsize = atoi(optarg) ;
993                         break;
994                 case 'W':
995                         wsize = atoi(optarg);
996                         break;
997                 case '1':
998                         if (isdigit(*optarg)) {
999                                 char *ep;
1000
1001                                 uid = strtoul(optarg, &ep, 10);
1002                                 if (*ep) {
1003                                         printf("bad uid value \"%s\"\n", optarg);
1004                                         exit(1);
1005                                 }
1006                         } else {
1007                                 struct passwd *pw;
1008
1009                                 if (!(pw = getpwnam(optarg))) {
1010                                         printf("bad user name \"%s\"\n", optarg);
1011                                         exit(1);
1012                                 }
1013                                 uid = pw->pw_uid;
1014                                 endpwent();
1015                         }
1016                         break;
1017                 case '2':
1018                         if (isdigit(*optarg)) {
1019                                 char *ep;
1020
1021                                 gid = strtoul(optarg, &ep, 10);
1022                                 if (*ep) {
1023                                         printf("bad gid value \"%s\"\n", optarg);
1024                                         exit(1);
1025                                 }
1026                         } else {
1027                                 struct group *gr;
1028
1029                                 if (!(gr = getgrnam(optarg))) {
1030                                         printf("bad user name \"%s\"\n", optarg);
1031                                         exit(1);
1032                                 }
1033                                 gid = gr->gr_gid;
1034                                 endpwent();
1035                         }
1036                         break;
1037                 case 'u':
1038                         got_user = 1;
1039                         user_name = optarg;
1040                         break;
1041                 case 'd':
1042                         domain_name = optarg; /* BB fix this - currently ignored */
1043                         got_domain = 1;
1044                         break;
1045                 case 'p':
1046                         if(mountpassword == NULL)
1047                                 mountpassword = (char *)calloc(65,1);
1048                         if(mountpassword) {
1049                                 got_password = 1;
1050                                 strncpy(mountpassword,optarg,64);
1051                         }
1052                         break;
1053                 case 'S':
1054                         get_password_from_file(0 /* stdin */,NULL);
1055                         break;
1056                 case 't':
1057                         break;
1058                 default:
1059                         printf("unknown mount option %c\n",c);
1060                         mount_cifs_usage();
1061                         exit(1);
1062                 }
1063         }
1064
1065         if((argc < 3) || (share_name == NULL) || (mountpoint == NULL)) {
1066                 mount_cifs_usage();
1067                 exit(1);
1068         }
1069
1070         if (getenv("PASSWD")) {
1071                 if(mountpassword == NULL)
1072                         mountpassword = (char *)calloc(65,1);
1073                 if(mountpassword) {
1074                         strncpy(mountpassword,getenv("PASSWD"),64);
1075                         got_password = 1;
1076                 }
1077         } else if (getenv("PASSWD_FD")) {
1078                 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1079         } else if (getenv("PASSWD_FILE")) {
1080                 get_password_from_file(0, getenv("PASSWD_FILE"));
1081         }
1082
1083         if (orgoptions && parse_options(&orgoptions, &flags)) {
1084                 rc = -1;
1085                 goto mount_exit;
1086         }
1087         ipaddr = parse_server(&share_name);
1088         if((ipaddr == NULL) && (got_ip == 0)) {
1089                 printf("No ip address specified and hostname not found\n");
1090                 rc = -1;
1091                 goto mount_exit;
1092         }
1093         
1094         /* BB save off path and pop after mount returns? */
1095         resolved_path = (char *)malloc(PATH_MAX+1);
1096         if(resolved_path) {
1097                 /* Note that if we can not canonicalize the name, we get
1098                 another chance to see if it is valid when we chdir to it */
1099                 if (realpath(mountpoint, resolved_path)) {
1100                         mountpoint = resolved_path; 
1101                 }
1102         }
1103         if(chdir(mountpoint)) {
1104                 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1105                 rc = -1;
1106                 goto mount_exit;
1107         }
1108
1109         if(stat (".", &statbuf)) {
1110                 printf("mount error: mount point %s does not exist\n",mountpoint);
1111                 rc = -1;
1112                 goto mount_exit;
1113         }
1114
1115         if (S_ISDIR(statbuf.st_mode) == 0) {
1116                 printf("mount error: mount point %s is not a directory\n",mountpoint);
1117                 rc = -1;
1118                 goto mount_exit;
1119         }
1120
1121         if((getuid() != 0) && (geteuid() == 0)) {
1122                 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1123 #ifndef CIFS_ALLOW_USR_SUID
1124                         /* Do not allow user mounts to control suid flag
1125                         for mount unless explicitly built that way */
1126                         flags |= MS_NOSUID | MS_NODEV;
1127 #endif                                          
1128                 } else {
1129                         printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n"); 
1130                         return -1;
1131                 }
1132         }
1133
1134         if(got_user == 0) {
1135                 user_name = getusername();
1136                 got_user = 1;
1137         }
1138        
1139         if(got_password == 0) {
1140                 mountpassword = getpass("Password: "); /* BB obsolete */
1141                 got_password = 1;
1142         }
1143         /* FIXME launch daemon (handles dfs name resolution and credential change) 
1144            remember to clear parms and overwrite password field before launching */
1145 mount_retry:
1146         if(orgoptions) {
1147                 optlen = strlen(orgoptions);
1148                 orgoptlen = optlen;
1149         } else
1150                 optlen = 0;
1151         if(share_name)
1152                 optlen += strlen(share_name) + 4;
1153         else {
1154                 printf("No server share name specified\n");
1155                 printf("\nMounting the DFS root for server not implemented yet\n");
1156                 exit(1);
1157         }
1158         if(user_name)
1159                 optlen += strlen(user_name) + 6;
1160         if(ipaddr)
1161                 optlen += strlen(ipaddr) + 4;
1162         if(mountpassword)
1163                 optlen += strlen(mountpassword) + 6;
1164         if(options)
1165                 free(options);
1166         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 */);
1167
1168         if(options == NULL) {
1169                 printf("Could not allocate memory for mount options\n");
1170                 return -1;
1171         }
1172                 
1173
1174         options[0] = 0;
1175         strncat(options,"unc=",4);
1176         strcat(options,share_name);
1177         /* scan backwards and reverse direction of slash */
1178         temp = strrchr(options, '/');
1179         if(temp > options + 6)
1180                 *temp = '\\';
1181         if(ipaddr) {
1182                 strncat(options,",ip=",4);
1183                 strcat(options,ipaddr);
1184         }
1185
1186         if(user_name) {
1187                 /* check for syntax like user=domain\user */
1188                 if(got_domain == 0)
1189                         domain_name = check_for_domain(&user_name);
1190                 strncat(options,",user=",6);
1191                 strcat(options,user_name);
1192         }
1193         if(retry == 0) {
1194                 if(domain_name) { 
1195                         /* extra length accounted for in option string above */
1196                         strncat(options,",domain=",8);
1197                         strcat(options,domain_name);
1198                 }
1199         }
1200         if(mountpassword) {
1201                 /* Commas have to be doubled, or else they will
1202                 look like the parameter separator */
1203 /*              if(sep is not set)*/
1204                 if(retry == 0)
1205                         check_for_comma(&mountpassword);
1206                 strncat(options,",pass=",6);
1207                 strcat(options,mountpassword);
1208         }
1209
1210         strncat(options,",ver=",5);
1211         strcat(options,MOUNT_CIFS_VERSION_MAJOR);
1212
1213         if(orgoptions) {
1214                 strcat(options,",");
1215                 strcat(options,orgoptions);
1216         }
1217         if(prefixpath) {
1218                 strncat(options,",prefixpath=",12);
1219                 strcat(options,prefixpath); /* no need to cat the / */
1220         }       
1221         if(verboseflag)
1222                 printf("\nmount.cifs kernel mount options %s \n",options);
1223         if(mount(share_name, mountpoint, "cifs", flags, options)) {
1224         /* remember to kill daemon on error */
1225                 char * tmp;
1226
1227                 switch (errno) {
1228                 case 0:
1229                         printf("mount failed but no error number set\n");
1230                         break;
1231                 case ENODEV:
1232                         printf("mount error: cifs filesystem not supported by the system\n");
1233                         break;
1234                 case ENXIO:
1235                         if(retry == 0) {
1236                                 retry = 1;
1237                                 tmp = share_name;
1238                                 while (*tmp && !(((unsigned char)tmp[0]) & 0x80)) {
1239                                         *tmp = toupper((unsigned char)*tmp);
1240                                         tmp++;
1241                                 }
1242                                 if(!*tmp) {
1243                                         printf("retrying with upper case share name\n");
1244                                         goto mount_retry;
1245                                 }
1246                         }
1247                 default:
1248                         printf("mount error %d = %s\n",errno,strerror(errno));
1249                 }
1250                 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1251                 rc = -1;
1252                 goto mount_exit;
1253         } else {
1254                 pmntfile = setmntent(MOUNTED, "a+");
1255                 if(pmntfile) {
1256                         mountent.mnt_fsname = share_name;
1257                         mountent.mnt_dir = mountpoint; 
1258                         mountent.mnt_type = CONST_DISCARD(char *,"cifs"); 
1259                         mountent.mnt_opts = (char *)malloc(220);
1260                         if(mountent.mnt_opts) {
1261                                 char * mount_user = getusername();
1262                                 memset(mountent.mnt_opts,0,200);
1263                                 if(flags & MS_RDONLY)
1264                                         strcat(mountent.mnt_opts,"ro");
1265                                 else
1266                                         strcat(mountent.mnt_opts,"rw");
1267                                 if(flags & MS_MANDLOCK)
1268                                         strcat(mountent.mnt_opts,",mand");
1269                                 if(flags & MS_NOEXEC)
1270                                         strcat(mountent.mnt_opts,",noexec");
1271                                 if(flags & MS_NOSUID)
1272                                         strcat(mountent.mnt_opts,",nosuid");
1273                                 if(flags & MS_NODEV)
1274                                         strcat(mountent.mnt_opts,",nodev");
1275                                 if(flags & MS_SYNCHRONOUS)
1276                                         strcat(mountent.mnt_opts,",synch");
1277                                 if(mount_user) {
1278                                         if(getuid() != 0) {
1279                                                 strcat(mountent.mnt_opts,",user=");
1280                                                 strcat(mountent.mnt_opts,mount_user);
1281                                         }
1282                                         /* free(mount_user); do not free static mem */
1283                                 }
1284                         }
1285                         mountent.mnt_freq = 0;
1286                         mountent.mnt_passno = 0;
1287                         rc = addmntent(pmntfile,&mountent);
1288                         endmntent(pmntfile);
1289                         if(mountent.mnt_opts)
1290                                 free(mountent.mnt_opts);
1291                 } else {
1292                     printf("could not update mount table\n");
1293                 }
1294         }
1295         rc = 0;
1296 mount_exit:
1297         if(mountpassword) {
1298                 int len = strlen(mountpassword);
1299                 memset(mountpassword,0,len);
1300                 free(mountpassword);
1301         }
1302
1303         if(options) {
1304                 memset(options,0,optlen);
1305                 free(options);
1306         }
1307
1308         if(orgoptions) {
1309                 memset(orgoptions,0,orgoptlen);
1310                 free(orgoptions);
1311         }
1312         if(resolved_path) {
1313                 free(resolved_path);
1314         }
1315
1316         if(free_share_name) {
1317                 free(share_name);
1318                 }
1319         return rc;
1320 }
1321