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