mount.cifs: Zero mountpassword content before freeing.
[Samba/gebeck_regimport.git] / source3 / client / mount.cifs.c
blob7432dac5be27cc94a4947170c3f8f23ab7ff1efe
1 /*
2 Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3 Copyright (C) 2003,2008 Steve French (sfrench@us.ibm.com)
4 Copyright (C) 2008 Jeremy Allison (jra@samba.org)
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
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>
41 #include <limits.h>
43 #define MOUNT_CIFS_VERSION_MAJOR "1"
44 #define MOUNT_CIFS_VERSION_MINOR "11"
46 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
47 #ifdef _SAMBA_BUILD_
48 #include "include/version.h"
49 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
50 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
51 #else
52 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
53 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
54 #else
55 #define MOUNT_CIFS_VENDOR_SUFFIX ""
56 #endif /* _SAMBA_BUILD_ */
57 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
59 #ifndef MS_MOVE
60 #define MS_MOVE 8192
61 #endif
63 #ifndef MS_BIND
64 #define MS_BIND 4096
65 #endif
67 #define MAX_UNC_LEN 1024
69 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
71 #ifndef SAFE_FREE
72 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
73 #endif
75 #define MOUNT_PASSWD_SIZE 64
76 #define DOMAIN_SIZE 64
78 const char *thisprogram;
79 int verboseflag = 0;
80 static int got_password = 0;
81 static int got_user = 0;
82 static int got_domain = 0;
83 static int got_ip = 0;
84 static int got_unc = 0;
85 static int got_uid = 0;
86 static int got_gid = 0;
87 static char * user_name = NULL;
88 static char * mountpassword = NULL;
89 char * domain_name = NULL;
90 char * prefixpath = NULL;
92 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
93 * don't link to libreplace so need them here. */
95 /* like strncpy but does not 0 fill the buffer and always null
96 * terminates. bufsize is the size of the destination buffer */
97 size_t strlcpy(char *d, const char *s, size_t bufsize)
99 size_t len = strlen(s);
100 size_t ret = len;
101 if (bufsize <= 0) return 0;
102 if (len >= bufsize) len = bufsize-1;
103 memcpy(d, s, len);
104 d[len] = 0;
105 return ret;
108 /* like strncat but does not 0 fill the buffer and always null
109 * terminates. bufsize is the length of the buffer, which should
110 * be one more than the maximum resulting string length */
111 size_t strlcat(char *d, const char *s, size_t bufsize)
113 size_t len1 = strlen(d);
114 size_t len2 = strlen(s);
115 size_t ret = len1 + len2;
117 if (len1+len2 >= bufsize) {
118 len2 = bufsize - (len1+1);
120 if (len2 > 0) {
121 memcpy(d+len1, s, len2);
122 d[len1+len2] = 0;
124 return ret;
127 /* BB finish BB
129 cifs_umount
130 open nofollow - avoid symlink exposure?
131 get owner of dir see if matches self or if root
132 call system(umount argv) etc.
134 BB end finish BB */
136 static char * check_for_domain(char **);
139 static void mount_cifs_usage(void)
141 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
142 printf("\nMount the remote target, specified as a UNC name,");
143 printf(" to a local directory.\n\nOptions:\n");
144 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
145 printf("\nLess commonly used options:");
146 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
147 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
148 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
149 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
150 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
151 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
152 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
153 printf("\n\nRarely used options:");
154 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
155 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
156 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
157 printf("\n\tin6_addr");
158 printf("\n\nOptions are described in more detail in the manual page");
159 printf("\n\tman 8 mount.cifs\n");
160 printf("\nTo display the version number of the mount helper:");
161 printf("\n\t%s -V\n",thisprogram);
163 if(mountpassword) {
164 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
166 SAFE_FREE(mountpassword);
167 exit(1);
170 /* caller frees username if necessary */
171 static char * getusername(void) {
172 char *username = NULL;
173 struct passwd *password = getpwuid(getuid());
175 if (password) {
176 username = password->pw_name;
178 return username;
181 static char * parse_cifs_url(char * unc_name)
183 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
184 return NULL;
187 static int open_cred_file(char * file_name)
189 char * line_buf;
190 char * temp_val;
191 FILE * fs;
192 int i, length;
193 fs = fopen(file_name,"r");
194 if(fs == NULL)
195 return errno;
196 line_buf = (char *)malloc(4096);
197 if(line_buf == NULL) {
198 fclose(fs);
199 return -ENOMEM;
202 while(fgets(line_buf,4096,fs)) {
203 /* parse line from credential file */
205 /* eat leading white space */
206 for(i=0;i<4086;i++) {
207 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
208 break;
209 /* if whitespace - skip past it */
211 if (strncasecmp("username",line_buf+i,8) == 0) {
212 temp_val = strchr(line_buf + i,'=');
213 if(temp_val) {
214 /* go past equals sign */
215 temp_val++;
216 for(length = 0;length<4087;length++) {
217 if ((temp_val[length] == '\n')
218 || (temp_val[length] == '\0')) {
219 break;
222 if(length > 4086) {
223 printf("mount.cifs failed due to malformed username in credentials file");
224 memset(line_buf,0,4096);
225 if(mountpassword) {
226 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
228 exit(1);
229 } else {
230 got_user = 1;
231 user_name = (char *)calloc(1 + length,1);
232 /* BB adding free of user_name string before exit,
233 not really necessary but would be cleaner */
234 strlcpy(user_name,temp_val, length+1);
237 } else if (strncasecmp("password",line_buf+i,8) == 0) {
238 temp_val = strchr(line_buf+i,'=');
239 if(temp_val) {
240 /* go past equals sign */
241 temp_val++;
242 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
243 if ((temp_val[length] == '\n')
244 || (temp_val[length] == '\0')) {
245 break;
248 if(length > MOUNT_PASSWD_SIZE) {
249 printf("mount.cifs failed: password in credentials file too long\n");
250 memset(line_buf,0, 4096);
251 exit(1);
252 } else {
253 if(mountpassword == NULL) {
254 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
255 } else
256 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
257 if(mountpassword) {
258 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
259 got_password = 1;
263 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
264 temp_val = strchr(line_buf+i,'=');
265 if(temp_val) {
266 /* go past equals sign */
267 temp_val++;
268 if(verboseflag)
269 printf("\nDomain %s\n",temp_val);
270 for(length = 0;length<DOMAIN_SIZE+1;length++) {
271 if ((temp_val[length] == '\n')
272 || (temp_val[length] == '\0')) {
273 break;
276 if(length > DOMAIN_SIZE) {
277 printf("mount.cifs failed: domain in credentials file too long\n");
278 exit(1);
279 } else {
280 if(domain_name == NULL) {
281 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
282 } else
283 memset(domain_name,0,DOMAIN_SIZE);
284 if(domain_name) {
285 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
286 got_domain = 1;
293 fclose(fs);
294 if(line_buf) {
295 memset(line_buf,0,4096);
297 SAFE_FREE(line_buf);
298 return 0;
301 static int get_password_from_file(int file_descript, char * filename)
303 int rc = 0;
304 int i;
305 char c;
307 if(mountpassword == NULL)
308 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
309 else
310 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
312 if (mountpassword == NULL) {
313 printf("malloc failed\n");
314 exit(1);
317 if(filename != NULL) {
318 file_descript = open(filename, O_RDONLY);
319 if(file_descript < 0) {
320 printf("mount.cifs failed. %s attempting to open password file %s\n",
321 strerror(errno),filename);
322 exit(1);
325 /* else file already open and fd provided */
327 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
328 rc = read(file_descript,&c,1);
329 if(rc < 0) {
330 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
331 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
332 if(filename != NULL)
333 close(file_descript);
334 exit(1);
335 } else if(rc == 0) {
336 if(mountpassword[0] == 0) {
337 if(verboseflag)
338 printf("\nWarning: null password used since cifs password file empty");
340 break;
341 } else /* read valid character */ {
342 if((c == 0) || (c == '\n')) {
343 break;
344 } else
345 mountpassword[i] = c;
348 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
349 printf("\nWarning: password longer than %d characters specified in cifs password file",
350 MOUNT_PASSWD_SIZE);
352 got_password = 1;
353 if(filename != NULL) {
354 close(file_descript);
357 return rc;
360 static int parse_options(char ** optionsp, int * filesys_flags)
362 const char * data;
363 char * percent_char = NULL;
364 char * value = NULL;
365 char * next_keyword = NULL;
366 char * out = NULL;
367 int out_len = 0;
368 int word_len;
369 int rc = 0;
370 char user[32];
371 char group[32];
373 if (!optionsp || !*optionsp)
374 return 1;
375 data = *optionsp;
377 if(verboseflag)
378 printf("parsing options: %s\n", data);
380 /* BB fixme check for separator override BB */
382 if (getuid()) {
383 got_uid = 1;
384 snprintf(user,sizeof(user),"%u",getuid());
385 got_gid = 1;
386 snprintf(group,sizeof(group),"%u",getgid());
389 /* while ((data = strsep(&options, ",")) != NULL) { */
390 while(data != NULL) {
391 /* check if ends with trailing comma */
392 if(*data == 0)
393 break;
395 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
396 /* data = next keyword */
397 /* value = next value ie stuff after equal sign */
399 next_keyword = strchr(data,','); /* BB handle sep= */
401 /* temporarily null terminate end of keyword=value pair */
402 if(next_keyword)
403 *next_keyword++ = 0;
405 /* temporarily null terminate keyword to make keyword and value distinct */
406 if ((value = strchr(data, '=')) != NULL) {
407 *value = '\0';
408 value++;
411 if (strncmp(data, "users",5) == 0) {
412 if(!value || !*value) {
413 goto nocopy;
415 } else if (strncmp(data, "user_xattr",10) == 0) {
416 /* do nothing - need to skip so not parsed as user name */
417 } else if (strncmp(data, "user", 4) == 0) {
419 if (!value || !*value) {
420 if(data[4] == '\0') {
421 if(verboseflag)
422 printf("\nskipping empty user mount parameter\n");
423 /* remove the parm since it would otherwise be confusing
424 to the kernel code which would think it was a real username */
425 goto nocopy;
426 } else {
427 printf("username specified with no parameter\n");
428 return 1; /* needs_arg; */
430 } else {
431 if (strnlen(value, 260) < 260) {
432 got_user=1;
433 percent_char = strchr(value,'%');
434 if(percent_char) {
435 *percent_char = ',';
436 if(mountpassword == NULL)
437 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
438 if(mountpassword) {
439 if(got_password)
440 printf("\nmount.cifs warning - password specified twice\n");
441 got_password = 1;
442 percent_char++;
443 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
444 /* remove password from username */
445 while(*percent_char != 0) {
446 *percent_char = ',';
447 percent_char++;
451 /* this is only case in which the user
452 name buf is not malloc - so we have to
453 check for domain name embedded within
454 the user name here since the later
455 call to check_for_domain will not be
456 invoked */
457 domain_name = check_for_domain(&value);
458 } else {
459 printf("username too long\n");
460 return 1;
463 } else if (strncmp(data, "pass", 4) == 0) {
464 if (!value || !*value) {
465 if(got_password) {
466 printf("\npassword specified twice, ignoring second\n");
467 } else
468 got_password = 1;
469 } else if (strnlen(value, 17) < 17) {
470 if(got_password)
471 printf("\nmount.cifs warning - password specified twice\n");
472 got_password = 1;
473 } else {
474 printf("password too long\n");
475 return 1;
477 } else if (strncmp(data, "sec", 3) == 0) {
478 if (value) {
479 if (!strcmp(value, "none"))
480 got_password = 1;
482 } else if (strncmp(data, "ip", 2) == 0) {
483 if (!value || !*value) {
484 printf("target ip address argument missing");
485 } else if (strnlen(value, 35) < 35) {
486 if(verboseflag)
487 printf("ip address %s override specified\n",value);
488 got_ip = 1;
489 } else {
490 printf("ip address too long\n");
491 return 1;
493 } else if ((strncmp(data, "unc", 3) == 0)
494 || (strncmp(data, "target", 6) == 0)
495 || (strncmp(data, "path", 4) == 0)) {
496 if (!value || !*value) {
497 printf("invalid path to network resource\n");
498 return 1; /* needs_arg; */
499 } else if(strnlen(value,5) < 5) {
500 printf("UNC name too short");
503 if (strnlen(value, 300) < 300) {
504 got_unc = 1;
505 if (strncmp(value, "//", 2) == 0) {
506 if(got_unc)
507 printf("unc name specified twice, ignoring second\n");
508 else
509 got_unc = 1;
510 } else if (strncmp(value, "\\\\", 2) != 0) {
511 printf("UNC Path does not begin with // or \\\\ \n");
512 return 1;
513 } else {
514 if(got_unc)
515 printf("unc name specified twice, ignoring second\n");
516 else
517 got_unc = 1;
519 } else {
520 printf("CIFS: UNC name too long\n");
521 return 1;
523 } else if ((strncmp(data, "domain", 3) == 0)
524 || (strncmp(data, "workgroup", 5) == 0)) {
525 if (!value || !*value) {
526 printf("CIFS: invalid domain name\n");
527 return 1; /* needs_arg; */
529 if (strnlen(value, 65) < 65) {
530 got_domain = 1;
531 } else {
532 printf("domain name too long\n");
533 return 1;
535 } else if (strncmp(data, "cred", 4) == 0) {
536 if (value && *value) {
537 rc = open_cred_file(value);
538 if(rc) {
539 printf("error %d opening credential file %s\n",rc, value);
540 return 1;
542 } else {
543 printf("invalid credential file name specified\n");
544 return 1;
546 } else if (strncmp(data, "uid", 3) == 0) {
547 if (value && *value) {
548 got_uid = 1;
549 if (!isdigit(*value)) {
550 struct passwd *pw;
552 if (!(pw = getpwnam(value))) {
553 printf("bad user name \"%s\"\n", value);
554 exit(1);
556 snprintf(user, sizeof(user), "%u", pw->pw_uid);
557 } else {
558 strlcpy(user,value,sizeof(user));
561 goto nocopy;
562 } else if (strncmp(data, "gid", 3) == 0) {
563 if (value && *value) {
564 got_gid = 1;
565 if (!isdigit(*value)) {
566 struct group *gr;
568 if (!(gr = getgrnam(value))) {
569 printf("bad group name \"%s\"\n", value);
570 exit(1);
572 snprintf(group, sizeof(group), "%u", gr->gr_gid);
573 } else {
574 strlcpy(group,value,sizeof(group));
577 goto nocopy;
578 /* fmask and dmask synonyms for people used to smbfs syntax */
579 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
580 if (!value || !*value) {
581 printf ("Option '%s' requires a numerical argument\n", data);
582 return 1;
585 if (value[0] != '0') {
586 printf ("WARNING: '%s' not expressed in octal.\n", data);
589 if (strcmp (data, "fmask") == 0) {
590 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
591 data = "file_mode"; /* BB fix this */
593 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
594 if (!value || !*value) {
595 printf ("Option '%s' requires a numerical argument\n", data);
596 return 1;
599 if (value[0] != '0') {
600 printf ("WARNING: '%s' not expressed in octal.\n", data);
603 if (strcmp (data, "dmask") == 0) {
604 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
605 data = "dir_mode";
607 /* the following eight mount options should be
608 stripped out from what is passed into the kernel
609 since these eight options are best passed as the
610 mount flags rather than redundantly to the kernel
611 and could generate spurious warnings depending on the
612 level of the corresponding cifs vfs kernel code */
613 } else if (strncmp(data, "nosuid", 6) == 0) {
614 *filesys_flags |= MS_NOSUID;
615 } else if (strncmp(data, "suid", 4) == 0) {
616 *filesys_flags &= ~MS_NOSUID;
617 } else if (strncmp(data, "nodev", 5) == 0) {
618 *filesys_flags |= MS_NODEV;
619 } else if ((strncmp(data, "nobrl", 5) == 0) ||
620 (strncmp(data, "nolock", 6) == 0)) {
621 *filesys_flags &= ~MS_MANDLOCK;
622 } else if (strncmp(data, "dev", 3) == 0) {
623 *filesys_flags &= ~MS_NODEV;
624 } else if (strncmp(data, "noexec", 6) == 0) {
625 *filesys_flags |= MS_NOEXEC;
626 } else if (strncmp(data, "exec", 4) == 0) {
627 *filesys_flags &= ~MS_NOEXEC;
628 } else if (strncmp(data, "guest", 5) == 0) {
629 got_password=1;
630 } else if (strncmp(data, "ro", 2) == 0) {
631 *filesys_flags |= MS_RDONLY;
632 } else if (strncmp(data, "rw", 2) == 0) {
633 *filesys_flags &= ~MS_RDONLY;
634 } else if (strncmp(data, "remount", 7) == 0) {
635 *filesys_flags |= MS_REMOUNT;
636 } /* else if (strnicmp(data, "port", 4) == 0) {
637 if (value && *value) {
638 vol->port =
639 simple_strtoul(value, &value, 0);
641 } else if (strnicmp(data, "rsize", 5) == 0) {
642 if (value && *value) {
643 vol->rsize =
644 simple_strtoul(value, &value, 0);
646 } else if (strnicmp(data, "wsize", 5) == 0) {
647 if (value && *value) {
648 vol->wsize =
649 simple_strtoul(value, &value, 0);
651 } else if (strnicmp(data, "version", 3) == 0) {
652 } else {
653 printf("CIFS: Unknown mount option %s\n",data);
654 } */ /* nothing to do on those four mount options above.
655 Just pass to kernel and ignore them here */
657 /* Copy (possibly modified) option to out */
658 word_len = strlen(data);
659 if (value)
660 word_len += 1 + strlen(value);
662 out = (char *)realloc(out, out_len + word_len + 2);
663 if (out == NULL) {
664 perror("malloc");
665 exit(1);
668 if (out_len) {
669 strlcat(out, ",", out_len + word_len + 2);
670 out_len++;
673 if (value)
674 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
675 else
676 snprintf(out + out_len, word_len + 1, "%s", data);
677 out_len = strlen(out);
679 nocopy:
680 data = next_keyword;
683 /* special-case the uid and gid */
684 if (got_uid) {
685 word_len = strlen(user);
687 out = (char *)realloc(out, out_len + word_len + 6);
688 if (out == NULL) {
689 perror("malloc");
690 exit(1);
693 if (out_len) {
694 strlcat(out, ",", out_len + word_len + 6);
695 out_len++;
697 snprintf(out + out_len, word_len + 5, "uid=%s", user);
698 out_len = strlen(out);
700 if (got_gid) {
701 word_len = strlen(group);
703 out = (char *)realloc(out, out_len + 1 + word_len + 6);
704 if (out == NULL) {
705 perror("malloc");
706 exit(1);
709 if (out_len) {
710 strlcat(out, ",", out_len + word_len + 6);
711 out_len++;
713 snprintf(out + out_len, word_len + 5, "gid=%s", group);
714 out_len = strlen(out);
717 SAFE_FREE(*optionsp);
718 *optionsp = out;
719 return 0;
722 /* replace all (one or more) commas with double commas */
723 static void check_for_comma(char ** ppasswrd)
725 char *new_pass_buf;
726 char *pass;
727 int i,j;
728 int number_of_commas = 0;
729 int len;
731 if(ppasswrd == NULL)
732 return;
733 else
734 (pass = *ppasswrd);
736 len = strlen(pass);
738 for(i=0;i<len;i++) {
739 if(pass[i] == ',')
740 number_of_commas++;
743 if(number_of_commas == 0)
744 return;
745 if(number_of_commas > MOUNT_PASSWD_SIZE) {
746 /* would otherwise overflow the mount options buffer */
747 printf("\nInvalid password. Password contains too many commas.\n");
748 return;
751 new_pass_buf = (char *)malloc(len+number_of_commas+1);
752 if(new_pass_buf == NULL)
753 return;
755 for(i=0,j=0;i<len;i++,j++) {
756 new_pass_buf[j] = pass[i];
757 if(pass[i] == ',') {
758 j++;
759 new_pass_buf[j] = pass[i];
762 new_pass_buf[len+number_of_commas] = 0;
764 SAFE_FREE(*ppasswrd);
765 *ppasswrd = new_pass_buf;
767 return;
770 /* Usernames can not have backslash in them and we use
771 [BB check if usernames can have forward slash in them BB]
772 backslash as domain\user separator character
774 static char * check_for_domain(char **ppuser)
776 char * original_string;
777 char * usernm;
778 char * domainnm;
779 int original_len;
780 int len;
781 int i;
783 if(ppuser == NULL)
784 return NULL;
786 original_string = *ppuser;
788 if (original_string == NULL)
789 return NULL;
791 original_len = strlen(original_string);
793 usernm = strchr(*ppuser,'/');
794 if (usernm == NULL) {
795 usernm = strchr(*ppuser,'\\');
796 if (usernm == NULL)
797 return NULL;
800 if(got_domain) {
801 printf("Domain name specified twice. Username probably malformed\n");
802 return NULL;
805 usernm[0] = 0;
806 domainnm = *ppuser;
807 if (domainnm[0] != 0) {
808 got_domain = 1;
809 } else {
810 printf("null domain\n");
812 len = strlen(domainnm);
813 /* reset domainm to new buffer, and copy
814 domain name into it */
815 domainnm = (char *)malloc(len+1);
816 if(domainnm == NULL)
817 return NULL;
819 strlcpy(domainnm,*ppuser,len+1);
821 /* move_string(*ppuser, usernm+1) */
822 len = strlen(usernm+1);
824 if(len >= original_len) {
825 /* should not happen */
826 return domainnm;
829 for(i=0;i<original_len;i++) {
830 if(i<len)
831 original_string[i] = usernm[i+1];
832 else /* stuff with commas to remove last parm */
833 original_string[i] = ',';
836 /* BB add check for more than one slash?
837 strchr(*ppuser,'/');
838 strchr(*ppuser,'\\')
841 return domainnm;
844 /* replace all occurances of "from" in a string with "to" */
845 static void replace_char(char *string, char from, char to, int maxlen)
847 char *lastchar = string + maxlen;
848 while (string) {
849 string = strchr(string, from);
850 if (string) {
851 *string = to;
852 if (string >= lastchar)
853 return;
858 /* Note that caller frees the returned buffer if necessary */
859 static char * parse_server(char ** punc_name)
861 char * unc_name = *punc_name;
862 int length = strnlen(unc_name, MAX_UNC_LEN);
863 char * share;
864 char * ipaddress_string = NULL;
865 struct hostent * host_entry = NULL;
866 struct in_addr server_ipaddr;
868 if(length > (MAX_UNC_LEN - 1)) {
869 printf("mount error: UNC name too long");
870 return NULL;
872 if (strncasecmp("cifs://",unc_name,7) == 0)
873 return parse_cifs_url(unc_name+7);
874 if (strncasecmp("smb://",unc_name,6) == 0) {
875 return parse_cifs_url(unc_name+6);
878 if(length < 3) {
879 /* BB add code to find DFS root here */
880 printf("\nMounting the DFS root for domain not implemented yet\n");
881 return NULL;
882 } else {
883 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
884 /* check for nfs syntax ie server:share */
885 share = strchr(unc_name,':');
886 if(share) {
887 *punc_name = (char *)malloc(length+3);
888 if(*punc_name == NULL) {
889 /* put the original string back if
890 no memory left */
891 *punc_name = unc_name;
892 return NULL;
894 *share = '/';
895 strlcpy((*punc_name)+2,unc_name,length+1);
896 SAFE_FREE(unc_name);
897 unc_name = *punc_name;
898 unc_name[length+2] = 0;
899 goto continue_unc_parsing;
900 } else {
901 printf("mount error: improperly formatted UNC name.");
902 printf(" %s does not begin with \\\\ or //\n",unc_name);
903 return NULL;
905 } else {
906 continue_unc_parsing:
907 unc_name[0] = '/';
908 unc_name[1] = '/';
909 unc_name += 2;
911 /* allow for either delimiter between host and sharename */
912 if ((share = strpbrk(unc_name, "/\\"))) {
913 *share = 0; /* temporarily terminate the string */
914 share += 1;
915 if(got_ip == 0) {
916 host_entry = gethostbyname(unc_name);
918 *(share - 1) = '/'; /* put delimiter back */
920 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
921 if ((prefixpath = strpbrk(share, "/\\"))) {
922 *prefixpath = 0; /* permanently terminate the string */
923 if (!strlen(++prefixpath))
924 prefixpath = NULL; /* this needs to be done explicitly */
926 if(got_ip) {
927 if(verboseflag)
928 printf("ip address specified explicitly\n");
929 return NULL;
931 if(host_entry == NULL) {
932 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
933 return NULL;
934 } else {
935 /* BB should we pass an alternate version of the share name as Unicode */
936 /* BB what about ipv6? BB */
937 /* BB add retries with alternate servers in list */
939 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
941 ipaddress_string = inet_ntoa(server_ipaddr);
942 if(ipaddress_string == NULL) {
943 printf("mount error: could not get valid ip address for target server\n");
944 return NULL;
946 return ipaddress_string;
948 } else {
949 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
950 printf("Mounting the DFS root for a particular server not implemented yet\n");
951 return NULL;
957 static struct option longopts[] = {
958 { "all", 0, NULL, 'a' },
959 { "help",0, NULL, 'h' },
960 { "move",0, NULL, 'm' },
961 { "bind",0, NULL, 'b' },
962 { "read-only", 0, NULL, 'r' },
963 { "ro", 0, NULL, 'r' },
964 { "verbose", 0, NULL, 'v' },
965 { "version", 0, NULL, 'V' },
966 { "read-write", 0, NULL, 'w' },
967 { "rw", 0, NULL, 'w' },
968 { "options", 1, NULL, 'o' },
969 { "type", 1, NULL, 't' },
970 { "rsize",1, NULL, 'R' },
971 { "wsize",1, NULL, 'W' },
972 { "uid", 1, NULL, '1'},
973 { "gid", 1, NULL, '2'},
974 { "user",1,NULL,'u'},
975 { "username",1,NULL,'u'},
976 { "dom",1,NULL,'d'},
977 { "domain",1,NULL,'d'},
978 { "password",1,NULL,'p'},
979 { "pass",1,NULL,'p'},
980 { "credentials",1,NULL,'c'},
981 { "port",1,NULL,'P'},
982 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
983 { NULL, 0, NULL, 0 }
986 /* convert a string to uppercase. return false if the string
987 * wasn't ASCII or was a NULL ptr */
988 static int
989 uppercase_string(char *string)
991 if (!string)
992 return 0;
994 while (*string) {
995 /* check for unicode */
996 if ((unsigned char) string[0] & 0x80)
997 return 0;
998 *string = toupper((unsigned char) *string);
999 string++;
1002 return 1;
1005 int main(int argc, char ** argv)
1007 int c;
1008 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1009 char * orgoptions = NULL;
1010 char * share_name = NULL;
1011 char * ipaddr = NULL;
1012 char * uuid = NULL;
1013 char * mountpoint = NULL;
1014 char * options = NULL;
1015 char * resolved_path = NULL;
1016 char * temp;
1017 char * dev_name;
1018 int rc;
1019 int rsize = 0;
1020 int wsize = 0;
1021 int nomtab = 0;
1022 int uid = 0;
1023 int gid = 0;
1024 int optlen = 0;
1025 int orgoptlen = 0;
1026 size_t options_size = 0;
1027 int retry = 0; /* set when we have to retry mount with uppercase */
1028 struct stat statbuf;
1029 struct utsname sysinfo;
1030 struct mntent mountent;
1031 FILE * pmntfile;
1033 /* setlocale(LC_ALL, "");
1034 bindtextdomain(PACKAGE, LOCALEDIR);
1035 textdomain(PACKAGE); */
1037 if(argc && argv) {
1038 thisprogram = argv[0];
1039 } else {
1040 mount_cifs_usage();
1041 exit(1);
1044 if(thisprogram == NULL)
1045 thisprogram = "mount.cifs";
1047 uname(&sysinfo);
1048 /* BB add workstation name and domain and pass down */
1050 /* #ifdef _GNU_SOURCE
1051 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1052 #endif */
1053 if(argc > 2) {
1054 dev_name = argv[1];
1055 share_name = strndup(argv[1], MAX_UNC_LEN);
1056 if (share_name == NULL) {
1057 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1058 exit(1);
1060 mountpoint = argv[2];
1061 } else {
1062 mount_cifs_usage();
1063 exit(1);
1066 /* add sharename in opts string as unc= parm */
1068 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1069 longopts, NULL)) != -1) {
1070 switch (c) {
1071 /* No code to do the following options yet */
1072 /* case 'l':
1073 list_with_volumelabel = 1;
1074 break;
1075 case 'L':
1076 volumelabel = optarg;
1077 break; */
1078 /* case 'a':
1079 ++mount_all;
1080 break; */
1082 case '?':
1083 case 'h': /* help */
1084 mount_cifs_usage ();
1085 exit(1);
1086 case 'n':
1087 ++nomtab;
1088 break;
1089 case 'b':
1090 #ifdef MS_BIND
1091 flags |= MS_BIND;
1092 #else
1093 fprintf(stderr,
1094 "option 'b' (MS_BIND) not supported\n");
1095 #endif
1096 break;
1097 case 'm':
1098 #ifdef MS_MOVE
1099 flags |= MS_MOVE;
1100 #else
1101 fprintf(stderr,
1102 "option 'm' (MS_MOVE) not supported\n");
1103 #endif
1104 break;
1105 case 'o':
1106 orgoptions = strdup(optarg);
1107 break;
1108 case 'r': /* mount readonly */
1109 flags |= MS_RDONLY;
1110 break;
1111 case 'U':
1112 uuid = optarg;
1113 break;
1114 case 'v':
1115 ++verboseflag;
1116 break;
1117 case 'V':
1118 printf ("mount.cifs version: %s.%s%s\n",
1119 MOUNT_CIFS_VERSION_MAJOR,
1120 MOUNT_CIFS_VERSION_MINOR,
1121 MOUNT_CIFS_VENDOR_SUFFIX);
1122 if(mountpassword) {
1123 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
1125 exit (0);
1126 case 'w':
1127 flags &= ~MS_RDONLY;
1128 break;
1129 case 'R':
1130 rsize = atoi(optarg) ;
1131 break;
1132 case 'W':
1133 wsize = atoi(optarg);
1134 break;
1135 case '1':
1136 if (isdigit(*optarg)) {
1137 char *ep;
1139 uid = strtoul(optarg, &ep, 10);
1140 if (*ep) {
1141 printf("bad uid value \"%s\"\n", optarg);
1142 exit(1);
1144 } else {
1145 struct passwd *pw;
1147 if (!(pw = getpwnam(optarg))) {
1148 printf("bad user name \"%s\"\n", optarg);
1149 exit(1);
1151 uid = pw->pw_uid;
1152 endpwent();
1154 break;
1155 case '2':
1156 if (isdigit(*optarg)) {
1157 char *ep;
1159 gid = strtoul(optarg, &ep, 10);
1160 if (*ep) {
1161 printf("bad gid value \"%s\"\n", optarg);
1162 exit(1);
1164 } else {
1165 struct group *gr;
1167 if (!(gr = getgrnam(optarg))) {
1168 printf("bad user name \"%s\"\n", optarg);
1169 exit(1);
1171 gid = gr->gr_gid;
1172 endpwent();
1174 break;
1175 case 'u':
1176 got_user = 1;
1177 user_name = optarg;
1178 break;
1179 case 'd':
1180 domain_name = optarg; /* BB fix this - currently ignored */
1181 got_domain = 1;
1182 break;
1183 case 'p':
1184 if(mountpassword == NULL)
1185 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1186 if(mountpassword) {
1187 got_password = 1;
1188 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1190 break;
1191 case 'S':
1192 get_password_from_file(0 /* stdin */,NULL);
1193 break;
1194 case 't':
1195 break;
1196 default:
1197 printf("unknown mount option %c\n",c);
1198 mount_cifs_usage();
1199 exit(1);
1203 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1204 mount_cifs_usage();
1205 exit(1);
1208 if (getenv("PASSWD")) {
1209 if(mountpassword == NULL)
1210 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1211 if(mountpassword) {
1212 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE);
1213 got_password = 1;
1215 } else if (getenv("PASSWD_FD")) {
1216 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1217 } else if (getenv("PASSWD_FILE")) {
1218 get_password_from_file(0, getenv("PASSWD_FILE"));
1221 if (orgoptions && parse_options(&orgoptions, &flags)) {
1222 rc = -1;
1223 goto mount_exit;
1225 ipaddr = parse_server(&share_name);
1226 if((ipaddr == NULL) && (got_ip == 0)) {
1227 printf("No ip address specified and hostname not found\n");
1228 rc = -1;
1229 goto mount_exit;
1232 /* BB save off path and pop after mount returns? */
1233 resolved_path = (char *)malloc(PATH_MAX+1);
1234 if(resolved_path) {
1235 /* Note that if we can not canonicalize the name, we get
1236 another chance to see if it is valid when we chdir to it */
1237 if (realpath(mountpoint, resolved_path)) {
1238 mountpoint = resolved_path;
1241 if(chdir(mountpoint)) {
1242 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1243 rc = -1;
1244 goto mount_exit;
1247 if(stat (".", &statbuf)) {
1248 printf("mount error: mount point %s does not exist\n",mountpoint);
1249 rc = -1;
1250 goto mount_exit;
1253 if (S_ISDIR(statbuf.st_mode) == 0) {
1254 printf("mount error: mount point %s is not a directory\n",mountpoint);
1255 rc = -1;
1256 goto mount_exit;
1259 if((getuid() != 0) && (geteuid() == 0)) {
1260 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1261 #ifndef CIFS_ALLOW_USR_SUID
1262 /* Do not allow user mounts to control suid flag
1263 for mount unless explicitly built that way */
1264 flags |= MS_NOSUID | MS_NODEV;
1265 #endif
1266 } else {
1267 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1268 return -1;
1272 if(got_user == 0) {
1273 user_name = getusername();
1274 got_user = 1;
1277 if(got_password == 0) {
1278 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1279 no good replacement yet. */
1280 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1281 if (!tmp_pass || !mountpassword) {
1282 printf("Password not entered, exiting\n");
1283 return -1;
1285 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1286 got_password = 1;
1288 /* FIXME launch daemon (handles dfs name resolution and credential change)
1289 remember to clear parms and overwrite password field before launching */
1290 mount_retry:
1291 if(orgoptions) {
1292 optlen = strlen(orgoptions);
1293 orgoptlen = optlen;
1294 } else
1295 optlen = 0;
1296 if(share_name)
1297 optlen += strlen(share_name) + 4;
1298 else {
1299 printf("No server share name specified\n");
1300 printf("\nMounting the DFS root for server not implemented yet\n");
1301 exit(1);
1303 if(user_name)
1304 optlen += strlen(user_name) + 6;
1305 if(ipaddr)
1306 optlen += strlen(ipaddr) + 4;
1307 if(mountpassword)
1308 optlen += strlen(mountpassword) + 6;
1309 SAFE_FREE(options);
1310 options_size = optlen + 10 + DOMAIN_SIZE;
1311 options = (char *)malloc(options_size /* space for commas in password */ + 8 /* space for domain= , domain name itself was counted as part of the length username string above */);
1313 if(options == NULL) {
1314 printf("Could not allocate memory for mount options\n");
1315 return -1;
1318 options[0] = 0;
1319 strlcpy(options,"unc=",options_size);
1320 strlcat(options,share_name,options_size);
1321 /* scan backwards and reverse direction of slash */
1322 temp = strrchr(options, '/');
1323 if(temp > options + 6)
1324 *temp = '\\';
1325 if(ipaddr) {
1326 strlcat(options,",ip=",options_size);
1327 strlcat(options,ipaddr,options_size);
1330 if(user_name) {
1331 /* check for syntax like user=domain\user */
1332 if(got_domain == 0)
1333 domain_name = check_for_domain(&user_name);
1334 strlcat(options,",user=",options_size);
1335 strlcat(options,user_name,options_size);
1337 if(retry == 0) {
1338 if(domain_name) {
1339 /* extra length accounted for in option string above */
1340 strlcat(options,",domain=",options_size);
1341 strlcat(options,domain_name,options_size);
1344 if(mountpassword) {
1345 /* Commas have to be doubled, or else they will
1346 look like the parameter separator */
1347 /* if(sep is not set)*/
1348 if(retry == 0)
1349 check_for_comma(&mountpassword);
1350 strlcat(options,",pass=",options_size);
1351 strlcat(options,mountpassword,options_size);
1354 strlcat(options,",ver=",options_size);
1355 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1357 if(orgoptions) {
1358 strlcat(options,",",options_size);
1359 strlcat(options,orgoptions,options_size);
1361 if(prefixpath) {
1362 strlcat(options,",prefixpath=",options_size);
1363 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1365 if(verboseflag)
1366 printf("\nmount.cifs kernel mount options %s \n",options);
1368 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1369 replace_char(dev_name, '\\', '/', strlen(share_name));
1371 if(mount(dev_name, mountpoint, "cifs", flags, options)) {
1372 /* remember to kill daemon on error */
1373 switch (errno) {
1374 case 0:
1375 printf("mount failed but no error number set\n");
1376 break;
1377 case ENODEV:
1378 printf("mount error: cifs filesystem not supported by the system\n");
1379 break;
1380 case ENXIO:
1381 if(retry == 0) {
1382 retry = 1;
1383 if (uppercase_string(dev_name) &&
1384 uppercase_string(share_name) &&
1385 uppercase_string(prefixpath)) {
1386 printf("retrying with upper case share name\n");
1387 goto mount_retry;
1390 default:
1391 printf("mount error %d = %s\n",errno,strerror(errno));
1393 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1394 rc = -1;
1395 goto mount_exit;
1396 } else {
1397 pmntfile = setmntent(MOUNTED, "a+");
1398 if(pmntfile) {
1399 mountent.mnt_fsname = dev_name;
1400 mountent.mnt_dir = mountpoint;
1401 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1402 mountent.mnt_opts = (char *)malloc(220);
1403 if(mountent.mnt_opts) {
1404 char * mount_user = getusername();
1405 memset(mountent.mnt_opts,0,200);
1406 if(flags & MS_RDONLY)
1407 strlcat(mountent.mnt_opts,"ro",220);
1408 else
1409 strlcat(mountent.mnt_opts,"rw",220);
1410 if(flags & MS_MANDLOCK)
1411 strlcat(mountent.mnt_opts,",mand",220);
1412 if(flags & MS_NOEXEC)
1413 strlcat(mountent.mnt_opts,",noexec",220);
1414 if(flags & MS_NOSUID)
1415 strlcat(mountent.mnt_opts,",nosuid",220);
1416 if(flags & MS_NODEV)
1417 strlcat(mountent.mnt_opts,",nodev",220);
1418 if(flags & MS_SYNCHRONOUS)
1419 strlcat(mountent.mnt_opts,",synch",220);
1420 if(mount_user) {
1421 if(getuid() != 0) {
1422 strlcat(mountent.mnt_opts,",user=",220);
1423 strlcat(mountent.mnt_opts,mount_user,220);
1425 /* free(mount_user); do not free static mem */
1428 mountent.mnt_freq = 0;
1429 mountent.mnt_passno = 0;
1430 rc = addmntent(pmntfile,&mountent);
1431 endmntent(pmntfile);
1432 SAFE_FREE(mountent.mnt_opts);
1433 } else {
1434 printf("could not update mount table\n");
1437 rc = 0;
1438 mount_exit:
1439 if(mountpassword) {
1440 int len = strlen(mountpassword);
1441 memset(mountpassword,0,len);
1442 SAFE_FREE(mountpassword);
1445 if(options) {
1446 memset(options,0,optlen);
1447 SAFE_FREE(options);
1450 if(orgoptions) {
1451 memset(orgoptions,0,orgoptlen);
1452 SAFE_FREE(orgoptions);
1455 SAFE_FREE(resolved_path);
1456 SAFE_FREE(share_name);
1457 return rc;