r22779: Patch for not prompting for password on cifs mounts when "sec=none"
[Samba/bb.git] / source3 / client / mount.cifs.c
blobcfcdaf997473b9ebaed445fd01a7ae4784c2601e
1 /*
2 Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3 Copyright (C) 2003,2005 Steve French (sfrench@us.ibm.com)
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.
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.
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. */
19 #ifndef _GNU_SOURCE
20 #define _GNU_SOURCE
21 #endif
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>
42 #define MOUNT_CIFS_VERSION_MAJOR "1"
43 #define MOUNT_CIFS_VERSION_MINOR "10"
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 */
58 #ifndef MS_MOVE
59 #define MS_MOVE 8192
60 #endif
62 #ifndef MS_BIND
63 #define MS_BIND 4096
64 #endif
66 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
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;
84 /* BB finish BB
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.
91 BB end finish BB */
93 static char * check_for_domain(char **);
96 static void mount_cifs_usage(void)
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);
118 if(mountpassword) {
119 memset(mountpassword,0,64);
120 free(mountpassword);
122 exit(1);
125 /* caller frees username if necessary */
126 static char * getusername(void) {
127 char *username = NULL;
128 struct passwd *password = getpwuid(getuid());
130 if (password) {
131 if(password->pw_name);
132 username = strdup(password->pw_name);
134 return username;
137 static char * parse_cifs_url(char * unc_name)
139 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
140 return NULL;
143 static int open_cred_file(char * file_name)
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;
158 while(fgets(line_buf,4096,fs)) {
159 /* parse line from credential file */
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 */
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;
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);
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);
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;
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);
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;
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;
229 if(length > 64) {
230 printf("mount.cifs failed: domain in credentials file too long\n");
231 if(mountpassword) {
232 memset(mountpassword,0,64);
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;
249 fclose(fs);
250 if(line_buf) {
251 memset(line_buf,0,4096);
252 free(line_buf);
254 return 0;
257 static int get_password_from_file(int file_descript, char * filename)
259 int rc = 0;
260 int i;
261 char c;
263 if(mountpassword == NULL)
264 mountpassword = (char *)calloc(65,1);
265 else
266 memset(mountpassword, 0, 64);
268 if (mountpassword == NULL) {
269 printf("malloc failed\n");
270 exit(1);
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);
281 /* else file already open and fd provided */
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");
296 break;
297 } else /* read valid character */ {
298 if((c == 0) || (c == '\n')) {
299 break;
300 } else
301 mountpassword[i] = c;
304 if((i == 64) && (verboseflag)) {
305 printf("\nWarning: password longer than 64 characters specified in cifs password file");
307 got_password = 1;
308 if(filename != NULL) {
309 close(file_descript);
312 return rc;
315 static int parse_options(char ** optionsp, int * filesys_flags)
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;
326 if (!optionsp || !*optionsp)
327 return 1;
328 data = *optionsp;
330 if(verboseflag)
331 printf("parsing options: %s\n", data);
333 /* BB fixme check for separator override BB */
335 /* while ((data = strsep(&options, ",")) != NULL) { */
336 while(data != NULL) {
337 /* check if ends with trailing comma */
338 if(*data == 0)
339 break;
341 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
342 /* data = next keyword */
343 /* value = next value ie stuff after equal sign */
345 next_keyword = strchr(data,','); /* BB handle sep= */
347 /* temporarily null terminate end of keyword=value pair */
348 if(next_keyword)
349 *next_keyword++ = 0;
351 /* temporarily null terminate keyword to make keyword and value distinct */
352 if ((value = strchr(data, '=')) != NULL) {
353 *value = '\0';
354 value++;
357 if (strncmp(data, "users",5) == 0) {
358 if(!value || !*value) {
359 goto nocopy;
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) {
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; */
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++;
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;
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;
423 } else if (strncmp(data, "sec", 3) == 0) {
424 if (value) {
425 if (!strcmp(value, "none"))
426 got_password = 1;
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;
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");
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;
465 } else {
466 printf("CIFS: UNC name too long\n");
467 return 1;
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; */
475 if (strnlen(value, 65) < 65) {
476 got_domain = 1;
477 } else {
478 printf("domain name too long\n");
479 return 1;
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;
488 } else {
489 printf("invalid credential file name specified\n");
490 return 1;
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];
499 if (!(pw = getpwnam(value))) {
500 printf("bad user name \"%s\"\n", value);
501 exit(1);
503 sprintf(temp, "%u", pw->pw_uid);
504 value = temp;
505 endpwent();
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];
515 if (!(gr = getgrnam(value))) {
516 printf("bad group name \"%s\"\n", value);
517 exit(1);
519 sprintf(temp, "%u", gr->gr_gid);
520 value = temp;
521 endpwent();
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;
531 if (value[0] != '0') {
532 printf ("WARNING: '%s' not expressed in octal.\n", data);
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 */
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;
545 if (value[0] != '0') {
546 printf ("WARNING: '%s' not expressed in octal.\n", data);
549 if (strcmp (data, "dmask") == 0) {
550 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
551 data = "dir_mode";
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);
587 } else if (strnicmp(data, "rsize", 5) == 0) {
588 if (value && *value) {
589 vol->rsize =
590 simple_strtoul(value, &value, 0);
592 } else if (strnicmp(data, "wsize", 5) == 0) {
593 if (value && *value) {
594 vol->wsize =
595 simple_strtoul(value, &value, 0);
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 */
603 /* Copy (possibly modified) option to out */
604 word_len = strlen(data);
605 if (value)
606 word_len += 1 + strlen(value);
608 out = (char *)realloc(out, out_len + word_len + 2);
609 if (out == NULL) {
610 perror("malloc");
611 exit(1);
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);
622 nocopy:
623 data = next_keyword;
625 free(*optionsp);
626 *optionsp = out;
627 return 0;
630 /* replace all (one or more) commas with double commas */
631 static void check_for_comma(char ** ppasswrd)
633 char *new_pass_buf;
634 char *pass;
635 int i,j;
636 int number_of_commas = 0;
637 int len;
639 if(ppasswrd == NULL)
640 return;
641 else
642 (pass = *ppasswrd);
644 len = strlen(pass);
646 for(i=0;i<len;i++) {
647 if(pass[i] == ',')
648 number_of_commas++;
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;
659 new_pass_buf = (char *)malloc(len+number_of_commas+1);
660 if(new_pass_buf == NULL)
661 return;
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];
670 new_pass_buf[len+number_of_commas] = 0;
672 free(*ppasswrd);
673 *ppasswrd = new_pass_buf;
675 return;
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
682 static char * check_for_domain(char **ppuser)
684 char * original_string;
685 char * usernm;
686 char * domainnm;
687 int original_len;
688 int len;
689 int i;
691 if(ppuser == NULL)
692 return NULL;
694 original_string = *ppuser;
696 if (original_string == NULL)
697 return NULL;
699 original_len = strlen(original_string);
701 usernm = strchr(*ppuser,'/');
702 if (usernm == NULL) {
703 usernm = strchr(*ppuser,'\\');
704 if (usernm == NULL)
705 return NULL;
708 if(got_domain) {
709 printf("Domain name specified twice. Username probably malformed\n");
710 return NULL;
713 usernm[0] = 0;
714 domainnm = *ppuser;
715 if (domainnm[0] != 0) {
716 got_domain = 1;
717 } else {
718 printf("null domain\n");
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;
727 strcpy(domainnm,*ppuser);
729 /* move_string(*ppuser, usernm+1) */
730 len = strlen(usernm+1);
732 if(len >= original_len) {
733 /* should not happen */
734 return domainnm;
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] = ',';
744 /* BB add check for more than one slash?
745 strchr(*ppuser,'/');
746 strchr(*ppuser,'\\')
749 return domainnm;
752 /* Note that caller frees the returned buffer if necessary */
753 static char * parse_server(char ** punc_name)
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;
762 if(length > 1023) {
763 printf("mount error: UNC name too long");
764 return NULL;
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);
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;
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;
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);
812 if(strnlen(unc_name, 16) < 16) {
813 servern = strdup(unc_name);
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 */
821 if(got_ip) {
822 if(verboseflag)
823 printf("ip address specified explicitly\n");
824 return NULL;
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 */
834 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
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;
841 return ipaddress_string;
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;
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 }
881 int main(int argc, char ** argv)
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;
908 /* setlocale(LC_ALL, "");
909 bindtextdomain(PACKAGE, LOCALEDIR);
910 textdomain(PACKAGE); */
912 if(argc && argv) {
913 thisprogram = argv[0];
914 } else {
915 mount_cifs_usage();
916 exit(1);
919 if(thisprogram == NULL)
920 thisprogram = "mount.cifs";
922 uname(&sysinfo);
923 /* BB add workstation name and domain and pass down */
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];
933 /* add sharename in opts string as unc= parm */
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; */
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);
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;
1006 uid = strtoul(optarg, &ep, 10);
1007 if (*ep) {
1008 printf("bad uid value \"%s\"\n", optarg);
1009 exit(1);
1011 } else {
1012 struct passwd *pw;
1014 if (!(pw = getpwnam(optarg))) {
1015 printf("bad user name \"%s\"\n", optarg);
1016 exit(1);
1018 uid = pw->pw_uid;
1019 endpwent();
1021 break;
1022 case '2':
1023 if (isdigit(*optarg)) {
1024 char *ep;
1026 gid = strtoul(optarg, &ep, 10);
1027 if (*ep) {
1028 printf("bad gid value \"%s\"\n", optarg);
1029 exit(1);
1031 } else {
1032 struct group *gr;
1034 if (!(gr = getgrnam(optarg))) {
1035 printf("bad user name \"%s\"\n", optarg);
1036 exit(1);
1038 gid = gr->gr_gid;
1039 endpwent();
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);
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);
1070 if((argc < 3) || (share_name == NULL) || (mountpoint == NULL)) {
1071 mount_cifs_usage();
1072 exit(1);
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;
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"));
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;
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;
1105 if(chdir(mountpoint)) {
1106 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1107 return -1;
1110 if(stat (".", &statbuf)) {
1111 printf("mount error: mount point %s does not exist\n",mountpoint);
1112 return -1;
1115 if (S_ISDIR(statbuf.st_mode) == 0) {
1116 printf("mount error: mount point %s is not a directory\n",mountpoint);
1117 return -1;
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;
1133 if(got_user == 0) {
1134 user_name = getusername();
1135 got_user = 1;
1138 if(got_password == 0) {
1139 mountpassword = getpass("Password: "); /* BB obsolete */
1140 got_password = 1;
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);
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 */
1166 free(options);
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;
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);
1190 if((servern) && retry_with_rfc1001name) {
1191 strcat(options, ",servern=");
1192 strcat(options, servern);
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);
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);
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);
1220 printf("\noptions2 %s at %p\n", options, options); /* BB removeme BB */
1222 strncat(options,",ver=",5);
1223 strcat(options,MOUNT_CIFS_VERSION_MAJOR);
1225 if(orgoptions) {
1226 strcat(options,",");
1227 strcat(options,orgoptions);
1230 printf("\noptions2 at %p\n", options); /* BB removeme BB */
1232 if(prefixpath) {
1233 strncat(options,",prefixpath=",12);
1234 strcat(options,prefixpath); /* no need to cat the / */
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;
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++;
1263 printf("Adding Netbios name of server to mount based on server part of UNC name\n");
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++;
1273 if(!*tmp) {
1274 printf("retrying with upper case share name\n");
1275 goto mount_retry;
1278 default:
1279 printf("mount error %d = %s\n",errno,strerror(errno));
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);
1313 free(mount_user);
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");
1326 rc = 0;
1327 mount_exit:
1328 if(mountpassword) {
1329 int len = strlen(mountpassword);
1330 memset(mountpassword,0,len);
1331 free(mountpassword);
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);
1341 if(orgoptions) {
1342 memset(orgoptions,0,orgoptlen);
1343 free(orgoptions);
1345 if(resolved_path) {
1346 free(resolved_path);
1349 if(servern) {
1350 free(servern);
1353 if(free_share_name) {
1354 free(share_name);
1356 if(user_name)
1357 free(user_name);
1359 return rc;