r14127: Remove coverity warning on mount.cifs.c
[kai/samba.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 #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\n");
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 = NULL;
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         } else {
891                 mount_cifs_usage();
892                 exit(1);
893         }
894
895         if(thisprogram == NULL)
896                 thisprogram = "mount.cifs";
897
898         uname(&sysinfo);
899         /* BB add workstation name and domain and pass down */
900
901 /* #ifdef _GNU_SOURCE
902         printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
903 #endif */
904         if(argc > 2) {
905                 share_name = argv[1];
906                 mountpoint = argv[2];
907         }
908
909         /* add sharename in opts string as unc= parm */
910
911         while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
912                          longopts, NULL)) != -1) {
913                 switch (c) {
914 /* No code to do the following  options yet */
915 /*      case 'l':
916                 list_with_volumelabel = 1;
917                 break;
918         case 'L':
919                 volumelabel = optarg;
920                 break; */
921 /*      case 'a':              
922                 ++mount_all;
923                 break; */
924
925                 case '?':
926                 case 'h':        /* help */
927                         mount_cifs_usage ();
928                         exit(1);
929                 case 'n':
930                     ++nomtab;
931                     break;
932                 case 'b':
933                         flags |= MS_BIND;
934                         break;
935                 case 'm':
936                         flags |= MS_MOVE;
937                         break;
938                 case 'o':
939                         orgoptions = strdup(optarg);
940                     break;
941                 case 'r':  /* mount readonly */
942                         flags |= MS_RDONLY;
943                         break;
944                 case 'U':
945                         uuid = optarg;
946                         break;
947                 case 'v':
948                         ++verboseflag;
949                         break;
950                 case 'V':          
951                         printf ("mount.cifs version: %s.%s%s\n",
952                         MOUNT_CIFS_VERSION_MAJOR,
953                         MOUNT_CIFS_VERSION_MINOR,
954                         MOUNT_CIFS_VENDOR_SUFFIX);
955                         if(mountpassword) {
956                                 memset(mountpassword,0,64);
957                         }
958                         exit (0);
959                 case 'w':
960                         flags &= ~MS_RDONLY;
961                         break;
962                 case 'R':
963                         rsize = atoi(optarg) ;
964                         break;
965                 case 'W':
966                         wsize = atoi(optarg);
967                         break;
968                 case '1':
969                         if (isdigit(*optarg)) {
970                                 char *ep;
971
972                                 uid = strtoul(optarg, &ep, 10);
973                                 if (*ep) {
974                                         printf("bad uid value \"%s\"\n", optarg);
975                                         exit(1);
976                                 }
977                         } else {
978                                 struct passwd *pw;
979
980                                 if (!(pw = getpwnam(optarg))) {
981                                         printf("bad user name \"%s\"\n", optarg);
982                                         exit(1);
983                                 }
984                                 uid = pw->pw_uid;
985                                 endpwent();
986                         }
987                         break;
988                 case '2':
989                         if (isdigit(*optarg)) {
990                                 char *ep;
991
992                                 gid = strtoul(optarg, &ep, 10);
993                                 if (*ep) {
994                                         printf("bad gid value \"%s\"\n", optarg);
995                                         exit(1);
996                                 }
997                         } else {
998                                 struct group *gr;
999
1000                                 if (!(gr = getgrnam(optarg))) {
1001                                         printf("bad user name \"%s\"\n", optarg);
1002                                         exit(1);
1003                                 }
1004                                 gid = gr->gr_gid;
1005                                 endpwent();
1006                         }
1007                         break;
1008                 case 'u':
1009                         got_user = 1;
1010                         user_name = optarg;
1011                         break;
1012                 case 'd':
1013                         domain_name = optarg; /* BB fix this - currently ignored */
1014                         got_domain = 1;
1015                         break;
1016                 case 'p':
1017                         if(mountpassword == NULL)
1018                                 mountpassword = calloc(65,1);
1019                         if(mountpassword) {
1020                                 got_password = 1;
1021                                 strncpy(mountpassword,optarg,64);
1022                         }
1023                         break;
1024                 case 'S':
1025                         get_password_from_file(0 /* stdin */,NULL);
1026                         break;
1027                 case 't':
1028                         break;
1029                 default:
1030                         printf("unknown mount option %c\n",c);
1031                         mount_cifs_usage();
1032                         exit(1);
1033                 }
1034         }
1035
1036         if((argc < 3) || (share_name == NULL) || (mountpoint == NULL)) {
1037                 mount_cifs_usage();
1038                 exit(1);
1039         }
1040
1041         if (getenv("PASSWD")) {
1042                 if(mountpassword == NULL)
1043                         mountpassword = calloc(65,1);
1044                 if(mountpassword) {
1045                         strncpy(mountpassword,getenv("PASSWD"),64);
1046                         got_password = 1;
1047                 }
1048         } else if (getenv("PASSWD_FD")) {
1049                 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1050         } else if (getenv("PASSWD_FILE")) {
1051                 get_password_from_file(0, getenv("PASSWD_FILE"));
1052         }
1053
1054         if (orgoptions && parse_options(&orgoptions, &flags))
1055                 return -1;
1056         ipaddr = parse_server(&share_name);
1057         if((ipaddr == NULL) && (got_ip == 0)) {
1058                 printf("No ip address specified and hostname not found\n");
1059                 return -1;
1060         }
1061         
1062         /* BB save off path and pop after mount returns? */
1063         resolved_path = malloc(PATH_MAX+1);
1064         if(resolved_path) {
1065                 /* Note that if we can not canonicalize the name, we get
1066                 another chance to see if it is valid when we chdir to it */
1067                 if (realpath(mountpoint, resolved_path)) {
1068                         mountpoint = resolved_path; 
1069                 }
1070         }
1071         if(chdir(mountpoint)) {
1072                 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1073                 return -1;
1074         }
1075
1076         if(stat (".", &statbuf)) {
1077                 printf("mount error: mount point %s does not exist\n",mountpoint);
1078                 return -1;
1079         }
1080
1081         if (S_ISDIR(statbuf.st_mode) == 0) {
1082                 printf("mount error: mount point %s is not a directory\n",mountpoint);
1083                 return -1;
1084         }
1085
1086         if((getuid() != 0) && (geteuid() == 0)) {
1087                 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1088 #ifndef CIFS_ALLOW_USR_SUID
1089                         /* Do not allow user mounts to control suid flag
1090                         for mount unless explicitly built that way */
1091                         flags |= MS_NOSUID | MS_NODEV;
1092 #endif                                          
1093                 } else {
1094                         printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n"); 
1095                         return -1;
1096                 }
1097         }
1098
1099         if(got_user == 0) {
1100                 user_name = getusername();
1101                 got_user = 1;
1102         }
1103        
1104         if(got_password == 0) {
1105                 mountpassword = getpass("Password: "); /* BB obsolete */
1106                 got_password = 1;
1107         }
1108         /* FIXME launch daemon (handles dfs name resolution and credential change) 
1109            remember to clear parms and overwrite password field before launching */
1110 mount_retry:
1111         if(orgoptions) {
1112                 optlen = strlen(orgoptions);
1113                 orgoptlen = optlen;
1114         } else
1115                 optlen = 0;
1116         if(share_name)
1117                 optlen += strlen(share_name) + 4;
1118         else {
1119                 printf("No server share name specified\n");
1120                 printf("\nMounting the DFS root for server not implemented yet\n");
1121                 exit(1);
1122         }
1123         if(user_name)
1124                 optlen += strlen(user_name) + 6;
1125         if(ipaddr)
1126                 optlen += strlen(ipaddr) + 4;
1127         if(mountpassword)
1128                 optlen += strlen(mountpassword) + 6;
1129         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 */);
1130
1131         if(options == NULL) {
1132                 printf("Could not allocate memory for mount options\n");
1133                 return -1;
1134         }
1135                 
1136
1137         options[0] = 0;
1138         strncat(options,"unc=",4);
1139         strcat(options,share_name);
1140         /* scan backwards and reverse direction of slash */
1141         temp = strrchr(options, '/');
1142         if(temp > options + 6)
1143                 *temp = '\\';
1144         if(ipaddr) {
1145                 strncat(options,",ip=",4);
1146                 strcat(options,ipaddr);
1147         }
1148
1149         if(user_name) {
1150                 /* check for syntax like user=domain\user */
1151                 if(got_domain == 0)
1152                         domain_name = check_for_domain(&user_name);
1153                 strncat(options,",user=",6);
1154                 strcat(options,user_name);
1155         }
1156         if(retry == 0) {
1157                 if(domain_name) { 
1158                         /* extra length accounted for in option string above */
1159                         strncat(options,",domain=",8);
1160                         strcat(options,domain_name);
1161                 }
1162         }
1163         if(mountpassword) {
1164                 /* Commas have to be doubled, or else they will
1165                 look like the parameter separator */
1166 /*              if(sep is not set)*/
1167                 if(retry == 0)
1168                         check_for_comma(&mountpassword);
1169                 strncat(options,",pass=",6);
1170                 strcat(options,mountpassword);
1171         }
1172
1173         strncat(options,",ver=",5);
1174         strcat(options,MOUNT_CIFS_VERSION_MAJOR);
1175
1176         if(orgoptions) {
1177                 strcat(options,",");
1178                 strcat(options,orgoptions);
1179         }
1180         if(verboseflag)
1181                 printf("\nmount.cifs kernel mount options %s \n",options);
1182         if(mount(share_name, mountpoint, "cifs", flags, options)) {
1183         /* remember to kill daemon on error */
1184                 char * tmp;
1185
1186                 switch (errno) {
1187                 case 0:
1188                         printf("mount failed but no error number set\n");
1189                         break;
1190                 case ENODEV:
1191                         printf("mount error: cifs filesystem not supported by the system\n");
1192                         break;
1193                 case ENXIO:
1194                         if(retry == 0) {
1195                                 retry = 1;
1196                                 tmp = share_name;
1197                                 while (*tmp && !(((unsigned char)tmp[0]) & 0x80)) {
1198                                         *tmp = toupper((unsigned char)*tmp);
1199                                         tmp++;
1200                                 }
1201                                 if(!*tmp) {
1202                                         printf("retrying with upper case share name\n");
1203                                         goto mount_retry;
1204                                 }
1205                         }
1206                 default:
1207                         
1208                         printf("mount error %d = %s\n",errno,strerror(errno));
1209                 }
1210                 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1211                 if(mountpassword) {
1212                         memset(mountpassword,0,64);
1213                 }
1214                 return -1;
1215         } else {
1216                 pmntfile = setmntent(MOUNTED, "a+");
1217                 if(pmntfile) {
1218                         mountent.mnt_fsname = share_name;
1219                         mountent.mnt_dir = mountpoint; 
1220                         mountent.mnt_type = CONST_DISCARD(char *,"cifs"); 
1221                         mountent.mnt_opts = malloc(220);
1222                         if(mountent.mnt_opts) {
1223                                 char * mount_user = getusername();
1224                                 memset(mountent.mnt_opts,0,200);
1225                                 if(flags & MS_RDONLY)
1226                                         strcat(mountent.mnt_opts,"ro");
1227                                 else
1228                                         strcat(mountent.mnt_opts,"rw");
1229                                 if(flags & MS_MANDLOCK)
1230                                         strcat(mountent.mnt_opts,",mand");
1231                                 if(flags & MS_NOEXEC)
1232                                         strcat(mountent.mnt_opts,",noexec");
1233                                 if(flags & MS_NOSUID)
1234                                         strcat(mountent.mnt_opts,",nosuid");
1235                                 if(flags & MS_NODEV)
1236                                         strcat(mountent.mnt_opts,",nodev");
1237                                 if(flags & MS_SYNCHRONOUS)
1238                                         strcat(mountent.mnt_opts,",synch");
1239                                 if(mount_user) {
1240                                         if(getuid() != 0) {
1241                                                 strcat(mountent.mnt_opts,",user=");
1242                                                 strcat(mountent.mnt_opts,mount_user);
1243                                         }
1244                                         free(mount_user);
1245                                 }
1246                         }
1247                         mountent.mnt_freq = 0;
1248                         mountent.mnt_passno = 0;
1249                         rc = addmntent(pmntfile,&mountent);
1250                         endmntent(pmntfile);
1251                         if(mountent.mnt_opts)
1252                                 free(mountent.mnt_opts);
1253                 } else {
1254                     printf("could not update mount table\n");
1255                 }
1256         }
1257         if(mountpassword) {
1258                 int len = strlen(mountpassword);
1259                 memset(mountpassword,0,len);
1260                 free(mountpassword);
1261         }
1262
1263         if(options) {
1264                 memset(options,0,optlen);
1265                 free(options);
1266         }
1267
1268         if(orgoptions) {
1269                 memset(orgoptions,0,orgoptlen);
1270                 free(orgoptions);
1271         }
1272         if(resolved_path) {
1273                 free(resolved_path);
1274         }
1275
1276         if(free_share_name) {
1277                 free(share_name);
1278                 }
1279         return 0;
1280 }
1281