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