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