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