s3-libnet: fix bug #6364: Pull realm from supplied username on libnet join
[Samba.git] / source3 / client / mount.cifs.c
blob1722eb033a1cc1e97640204c0a735b9dc197f8c5
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>
42 #include <fstab.h>
43 #include "mount.h"
45 #define MOUNT_CIFS_VERSION_MAJOR "1"
46 #define MOUNT_CIFS_VERSION_MINOR "14"
48 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
49 #ifdef _SAMBA_BUILD_
50 #include "include/version.h"
51 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
52 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
53 #else
54 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
55 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
56 #else
57 #define MOUNT_CIFS_VENDOR_SUFFIX ""
58 #endif /* _SAMBA_BUILD_ */
59 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
61 #ifdef _SAMBA_BUILD_
62 #include "include/config.h"
63 #endif
65 #ifndef MS_MOVE
66 #define MS_MOVE 8192
67 #endif
69 #ifndef MS_BIND
70 #define MS_BIND 4096
71 #endif
73 /* private flags - clear these before passing to kernel */
74 #define MS_USERS 0x40000000
75 #define MS_USER 0x80000000
77 #define MAX_UNC_LEN 1024
79 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
81 #ifndef SAFE_FREE
82 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
83 #endif
85 #define MOUNT_PASSWD_SIZE 64
86 #define DOMAIN_SIZE 64
88 /* currently maximum length of IPv6 address string */
89 #define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
92 * mount.cifs has been the subject of many "security" bugs that have arisen
93 * because of users and distributions installing it as a setuid root program.
94 * mount.cifs has not been audited for security. Thus, we strongly recommend
95 * that it not be installed setuid root. To make that abundantly clear,
96 * mount.cifs now check whether it's running setuid root and exit with an
97 * error if it is. If you wish to disable this check, then set the following
98 * #define to 1, but please realize that you do so at your own peril.
100 #define CIFS_DISABLE_SETUID_CHECK 0
103 * By default, mount.cifs follows the conventions set forth by /bin/mount
104 * for user mounts. That is, it requires that the mount be listed in
105 * /etc/fstab with the "user" option when run as an unprivileged user and
106 * mount.cifs is setuid root.
108 * Older versions of mount.cifs however were "looser" in this regard. When
109 * made setuid root, a user could run mount.cifs directly and mount any share
110 * on a directory owned by that user.
112 * The legacy behavior is now disabled by default. To reenable it, set the
113 * following #define to true.
115 #define CIFS_LEGACY_SETUID_CHECK 0
118 * When an unprivileged user runs a setuid mount.cifs, we set certain mount
119 * flags by default. These defaults can be changed here.
121 #define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
123 const char *thisprogram;
124 int verboseflag = 0;
125 int fakemnt = 0;
126 static int got_password = 0;
127 static int got_user = 0;
128 static int got_domain = 0;
129 static int got_ip = 0;
130 static int got_unc = 0;
131 static int got_uid = 0;
132 static int got_gid = 0;
133 static char * user_name = NULL;
134 static char * mountpassword = NULL;
135 char * domain_name = NULL;
136 char * prefixpath = NULL;
138 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
139 * don't link to libreplace so need them here. */
141 /* like strncpy but does not 0 fill the buffer and always null
142 * terminates. bufsize is the size of the destination buffer */
144 #ifndef HAVE_STRLCPY
145 static size_t strlcpy(char *d, const char *s, size_t bufsize)
147 size_t len = strlen(s);
148 size_t ret = len;
149 if (bufsize <= 0) return 0;
150 if (len >= bufsize) len = bufsize-1;
151 memcpy(d, s, len);
152 d[len] = 0;
153 return ret;
155 #endif
157 /* like strncat but does not 0 fill the buffer and always null
158 * terminates. bufsize is the length of the buffer, which should
159 * be one more than the maximum resulting string length */
161 #ifndef HAVE_STRLCAT
162 static size_t strlcat(char *d, const char *s, size_t bufsize)
164 size_t len1 = strlen(d);
165 size_t len2 = strlen(s);
166 size_t ret = len1 + len2;
168 if (len1+len2 >= bufsize) {
169 if (bufsize < (len1+1)) {
170 return ret;
172 len2 = bufsize - (len1+1);
174 if (len2 > 0) {
175 memcpy(d+len1, s, len2);
176 d[len1+len2] = 0;
178 return ret;
180 #endif
183 * If an unprivileged user is doing the mounting then we need to ensure
184 * that the entry is in /etc/fstab.
186 static int
187 check_mountpoint(const char *progname, char *mountpoint)
189 int err;
190 struct stat statbuf;
192 /* does mountpoint exist and is it a directory? */
193 err = stat(".", &statbuf);
194 if (err) {
195 fprintf(stderr, "%s: failed to stat %s: %s\n", progname,
196 mountpoint, strerror(errno));
197 return EX_USAGE;
200 if (!S_ISDIR(statbuf.st_mode)) {
201 fprintf(stderr, "%s: %s is not a directory!", progname,
202 mountpoint);
203 return EX_USAGE;
206 #if CIFS_LEGACY_SETUID_CHECK
207 /* do extra checks on mountpoint for legacy setuid behavior */
208 if (!getuid() || geteuid())
209 return 0;
211 if (statbuf.st_uid != getuid()) {
212 fprintf(stderr, "%s: %s is not owned by user\n", progname,
213 mountpoint);
214 return EX_USAGE;
217 if ((statbuf.st_mode & S_IRWXU) != S_IRWXU) {
218 fprintf(stderr, "%s: invalid permissions on %s\n", progname,
219 mountpoint);
220 return EX_USAGE;
222 #endif /* CIFS_LEGACY_SETUID_CHECK */
224 return 0;
227 #if CIFS_DISABLE_SETUID_CHECK
228 static int
229 check_setuid(void)
231 return 0;
233 #else /* CIFS_DISABLE_SETUID_CHECK */
234 static int
235 check_setuid(void)
237 if (getuid() && !geteuid()) {
238 printf("This mount.cifs program has been built with the "
239 "ability to run as a setuid root program disabled.\n"
240 "mount.cifs has not been well audited for security "
241 "holes. Therefore the Samba team does not recommend "
242 "installing it as a setuid root program.\n");
243 return 1;
246 return 0;
248 #endif /* CIFS_DISABLE_SETUID_CHECK */
250 #if CIFS_LEGACY_SETUID_CHECK
251 static int
252 check_fstab(const char *progname, char *mountpoint, char *devname,
253 char **options)
255 return 0;
257 #else /* CIFS_LEGACY_SETUID_CHECK */
258 static int
259 check_fstab(const char *progname, char *mountpoint, char *devname,
260 char **options)
262 FILE *fstab;
263 struct mntent *mnt;
265 /* make sure this mount is listed in /etc/fstab */
266 fstab = setmntent(_PATH_FSTAB, "r");
267 if (!fstab) {
268 fprintf(stderr, "Couldn't open %s for reading!\n",
269 _PATH_FSTAB);
270 return EX_FILEIO;
273 while((mnt = getmntent(fstab))) {
274 if (!strcmp(mountpoint, mnt->mnt_dir))
275 break;
277 endmntent(fstab);
279 if (mnt == NULL || strcmp(mnt->mnt_fsname, devname)) {
280 fprintf(stderr, "%s: permission denied: no match for "
281 "%s found in %s\n", progname, mountpoint,
282 _PATH_FSTAB);
283 return EX_USAGE;
287 * 'mount' munges the options from fstab before passing them
288 * to us. It is non-trivial to test that we have the correct
289 * set of options. We don't want to trust what the user
290 * gave us, so just take whatever is in /etc/fstab.
292 free(*options);
293 *options = strdup(mnt->mnt_opts);
294 return 0;
296 #endif /* CIFS_LEGACY_SETUID_CHECK */
298 /* BB finish BB
300 cifs_umount
301 open nofollow - avoid symlink exposure?
302 get owner of dir see if matches self or if root
303 call system(umount argv) etc.
305 BB end finish BB */
307 static char * check_for_domain(char **);
310 static void mount_cifs_usage(void)
312 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
313 printf("\nMount the remote target, specified as a UNC name,");
314 printf(" to a local directory.\n\nOptions:\n");
315 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
316 printf("\nLess commonly used options:");
317 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
318 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
319 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
320 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
321 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
322 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
323 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
324 printf("\n\nRarely used options:");
325 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
326 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
327 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
328 printf("\n\tin6_addr");
329 printf("\n\nOptions are described in more detail in the manual page");
330 printf("\n\tman 8 mount.cifs\n");
331 printf("\nTo display the version number of the mount helper:");
332 printf("\n\t%s -V\n",thisprogram);
334 SAFE_FREE(mountpassword);
337 /* caller frees username if necessary */
338 static char * getusername(void) {
339 char *username = NULL;
340 struct passwd *password = getpwuid(getuid());
342 if (password) {
343 username = password->pw_name;
345 return username;
348 static int open_cred_file(char * file_name)
350 char * line_buf;
351 char * temp_val;
352 FILE * fs;
353 int i, length;
355 i = access(file_name, R_OK);
356 if (i)
357 return i;
359 fs = fopen(file_name,"r");
360 if(fs == NULL)
361 return errno;
362 line_buf = (char *)malloc(4096);
363 if(line_buf == NULL) {
364 fclose(fs);
365 return ENOMEM;
368 while(fgets(line_buf,4096,fs)) {
369 /* parse line from credential file */
371 /* eat leading white space */
372 for(i=0;i<4086;i++) {
373 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
374 break;
375 /* if whitespace - skip past it */
377 if (strncasecmp("username",line_buf+i,8) == 0) {
378 temp_val = strchr(line_buf + i,'=');
379 if(temp_val) {
380 /* go past equals sign */
381 temp_val++;
382 for(length = 0;length<4087;length++) {
383 if ((temp_val[length] == '\n')
384 || (temp_val[length] == '\0')) {
385 temp_val[length] = '\0';
386 break;
389 if(length > 4086) {
390 printf("mount.cifs failed due to malformed username in credentials file");
391 memset(line_buf,0,4096);
392 exit(EX_USAGE);
393 } else {
394 got_user = 1;
395 user_name = (char *)calloc(1 + length,1);
396 /* BB adding free of user_name string before exit,
397 not really necessary but would be cleaner */
398 strlcpy(user_name,temp_val, length+1);
401 } else if (strncasecmp("password",line_buf+i,8) == 0) {
402 temp_val = strchr(line_buf+i,'=');
403 if(temp_val) {
404 /* go past equals sign */
405 temp_val++;
406 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
407 if ((temp_val[length] == '\n')
408 || (temp_val[length] == '\0')) {
409 temp_val[length] = '\0';
410 break;
413 if(length > MOUNT_PASSWD_SIZE) {
414 printf("mount.cifs failed: password in credentials file too long\n");
415 memset(line_buf,0, 4096);
416 exit(EX_USAGE);
417 } else {
418 if(mountpassword == NULL) {
419 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
420 } else
421 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
422 if(mountpassword) {
423 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
424 got_password = 1;
428 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
429 temp_val = strchr(line_buf+i,'=');
430 if(temp_val) {
431 /* go past equals sign */
432 temp_val++;
433 if(verboseflag)
434 printf("\nDomain %s\n",temp_val);
435 for(length = 0;length<DOMAIN_SIZE+1;length++) {
436 if ((temp_val[length] == '\n')
437 || (temp_val[length] == '\0')) {
438 temp_val[length] = '\0';
439 break;
442 if(length > DOMAIN_SIZE) {
443 printf("mount.cifs failed: domain in credentials file too long\n");
444 exit(EX_USAGE);
445 } else {
446 if(domain_name == NULL) {
447 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
448 } else
449 memset(domain_name,0,DOMAIN_SIZE);
450 if(domain_name) {
451 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
452 got_domain = 1;
459 fclose(fs);
460 SAFE_FREE(line_buf);
461 return 0;
464 static int get_password_from_file(int file_descript, char * filename)
466 int rc = 0;
467 int i;
468 char c;
470 if(mountpassword == NULL)
471 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
472 else
473 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
475 if (mountpassword == NULL) {
476 printf("malloc failed\n");
477 exit(EX_SYSERR);
480 if(filename != NULL) {
481 rc = access(filename, R_OK);
482 if (rc) {
483 fprintf(stderr, "mount.cifs failed: access check of %s failed: %s\n",
484 filename, strerror(errno));
485 exit(EX_SYSERR);
487 file_descript = open(filename, O_RDONLY);
488 if(file_descript < 0) {
489 printf("mount.cifs failed. %s attempting to open password file %s\n",
490 strerror(errno),filename);
491 exit(EX_SYSERR);
494 /* else file already open and fd provided */
496 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
497 rc = read(file_descript,&c,1);
498 if(rc < 0) {
499 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
500 if(filename != NULL)
501 close(file_descript);
502 exit(EX_SYSERR);
503 } else if(rc == 0) {
504 if(mountpassword[0] == 0) {
505 if(verboseflag)
506 printf("\nWarning: null password used since cifs password file empty");
508 break;
509 } else /* read valid character */ {
510 if((c == 0) || (c == '\n')) {
511 mountpassword[i] = '\0';
512 break;
513 } else
514 mountpassword[i] = c;
517 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
518 printf("\nWarning: password longer than %d characters specified in cifs password file",
519 MOUNT_PASSWD_SIZE);
521 got_password = 1;
522 if(filename != NULL) {
523 close(file_descript);
526 return rc;
529 static int parse_options(char ** optionsp, unsigned long * filesys_flags)
531 const char * data;
532 char * percent_char = NULL;
533 char * value = NULL;
534 char * next_keyword = NULL;
535 char * out = NULL;
536 int out_len = 0;
537 int word_len;
538 int rc = 0;
539 char user[32];
540 char group[32];
542 if (!optionsp || !*optionsp)
543 return 1;
544 data = *optionsp;
546 /* BB fixme check for separator override BB */
548 if (getuid()) {
549 got_uid = 1;
550 snprintf(user,sizeof(user),"%u",getuid());
551 got_gid = 1;
552 snprintf(group,sizeof(group),"%u",getgid());
555 /* while ((data = strsep(&options, ",")) != NULL) { */
556 while(data != NULL) {
557 /* check if ends with trailing comma */
558 if(*data == 0)
559 break;
561 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
562 /* data = next keyword */
563 /* value = next value ie stuff after equal sign */
565 next_keyword = strchr(data,','); /* BB handle sep= */
567 /* temporarily null terminate end of keyword=value pair */
568 if(next_keyword)
569 *next_keyword++ = 0;
571 /* temporarily null terminate keyword to make keyword and value distinct */
572 if ((value = strchr(data, '=')) != NULL) {
573 *value = '\0';
574 value++;
577 if (strncmp(data, "users",5) == 0) {
578 if(!value || !*value) {
579 *filesys_flags |= MS_USERS;
580 goto nocopy;
582 } else if (strncmp(data, "user_xattr",10) == 0) {
583 /* do nothing - need to skip so not parsed as user name */
584 } else if (strncmp(data, "user", 4) == 0) {
586 if (!value || !*value) {
587 if(data[4] == '\0') {
588 *filesys_flags |= MS_USER;
589 goto nocopy;
590 } else {
591 printf("username specified with no parameter\n");
592 SAFE_FREE(out);
593 return 1; /* needs_arg; */
595 } else {
596 if (strnlen(value, 260) < 260) {
597 got_user=1;
598 percent_char = strchr(value,'%');
599 if(percent_char) {
600 *percent_char = ',';
601 if(mountpassword == NULL)
602 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
603 if(mountpassword) {
604 if(got_password)
605 printf("\nmount.cifs warning - password specified twice\n");
606 got_password = 1;
607 percent_char++;
608 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
609 /* remove password from username */
610 while(*percent_char != 0) {
611 *percent_char = ',';
612 percent_char++;
616 /* this is only case in which the user
617 name buf is not malloc - so we have to
618 check for domain name embedded within
619 the user name here since the later
620 call to check_for_domain will not be
621 invoked */
622 domain_name = check_for_domain(&value);
623 } else {
624 printf("username too long\n");
625 SAFE_FREE(out);
626 return 1;
629 } else if (strncmp(data, "pass", 4) == 0) {
630 if (!value || !*value) {
631 if(got_password) {
632 fprintf(stderr, "\npassword specified twice, ignoring second\n");
633 } else
634 got_password = 1;
635 } else if (strnlen(value, MOUNT_PASSWD_SIZE) < MOUNT_PASSWD_SIZE) {
636 if (got_password) {
637 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
638 } else {
639 mountpassword = strndup(value, MOUNT_PASSWD_SIZE);
640 if (!mountpassword) {
641 fprintf(stderr, "mount.cifs error: %s", strerror(ENOMEM));
642 SAFE_FREE(out);
643 return 1;
645 got_password = 1;
647 } else {
648 fprintf(stderr, "password too long\n");
649 SAFE_FREE(out);
650 return 1;
652 goto nocopy;
653 } else if (strncmp(data, "sec", 3) == 0) {
654 if (value) {
655 if (!strncmp(value, "none", 4) ||
656 !strncmp(value, "krb5", 4))
657 got_password = 1;
659 } else if (strncmp(data, "ip", 2) == 0) {
660 if (!value || !*value) {
661 printf("target ip address argument missing");
662 } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
663 if(verboseflag)
664 printf("ip address %s override specified\n",value);
665 got_ip = 1;
666 } else {
667 printf("ip address too long\n");
668 SAFE_FREE(out);
669 return 1;
671 } else if ((strncmp(data, "unc", 3) == 0)
672 || (strncmp(data, "target", 6) == 0)
673 || (strncmp(data, "path", 4) == 0)) {
674 if (!value || !*value) {
675 printf("invalid path to network resource\n");
676 SAFE_FREE(out);
677 return 1; /* needs_arg; */
678 } else if(strnlen(value,5) < 5) {
679 printf("UNC name too short");
682 if (strnlen(value, 300) < 300) {
683 got_unc = 1;
684 if (strncmp(value, "//", 2) == 0) {
685 if(got_unc)
686 printf("unc name specified twice, ignoring second\n");
687 else
688 got_unc = 1;
689 } else if (strncmp(value, "\\\\", 2) != 0) {
690 printf("UNC Path does not begin with // or \\\\ \n");
691 SAFE_FREE(out);
692 return 1;
693 } else {
694 if(got_unc)
695 printf("unc name specified twice, ignoring second\n");
696 else
697 got_unc = 1;
699 } else {
700 printf("CIFS: UNC name too long\n");
701 SAFE_FREE(out);
702 return 1;
704 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
705 || (strncmp(data, "workg", 5) == 0)) {
706 /* note this allows for synonyms of "domain"
707 such as "DOM" and "dom" and "workgroup"
708 and "WORKGRP" etc. */
709 if (!value || !*value) {
710 printf("CIFS: invalid domain name\n");
711 SAFE_FREE(out);
712 return 1; /* needs_arg; */
714 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
715 got_domain = 1;
716 } else {
717 printf("domain name too long\n");
718 SAFE_FREE(out);
719 return 1;
721 } else if (strncmp(data, "cred", 4) == 0) {
722 if (value && *value) {
723 rc = open_cred_file(value);
724 if(rc) {
725 printf("error %d (%s) opening credential file %s\n",
726 rc, strerror(rc), value);
727 SAFE_FREE(out);
728 return 1;
730 } else {
731 printf("invalid credential file name specified\n");
732 SAFE_FREE(out);
733 return 1;
735 } else if (strncmp(data, "uid", 3) == 0) {
736 if (value && *value) {
737 got_uid = 1;
738 if (!isdigit(*value)) {
739 struct passwd *pw;
741 if (!(pw = getpwnam(value))) {
742 printf("bad user name \"%s\"\n", value);
743 exit(EX_USAGE);
745 snprintf(user, sizeof(user), "%u", pw->pw_uid);
746 } else {
747 strlcpy(user,value,sizeof(user));
750 goto nocopy;
751 } else if (strncmp(data, "gid", 3) == 0) {
752 if (value && *value) {
753 got_gid = 1;
754 if (!isdigit(*value)) {
755 struct group *gr;
757 if (!(gr = getgrnam(value))) {
758 printf("bad group name \"%s\"\n", value);
759 exit(EX_USAGE);
761 snprintf(group, sizeof(group), "%u", gr->gr_gid);
762 } else {
763 strlcpy(group,value,sizeof(group));
766 goto nocopy;
767 /* fmask and dmask synonyms for people used to smbfs syntax */
768 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
769 if (!value || !*value) {
770 printf ("Option '%s' requires a numerical argument\n", data);
771 SAFE_FREE(out);
772 return 1;
775 if (value[0] != '0') {
776 printf ("WARNING: '%s' not expressed in octal.\n", data);
779 if (strcmp (data, "fmask") == 0) {
780 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
781 data = "file_mode"; /* BB fix this */
783 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
784 if (!value || !*value) {
785 printf ("Option '%s' requires a numerical argument\n", data);
786 SAFE_FREE(out);
787 return 1;
790 if (value[0] != '0') {
791 printf ("WARNING: '%s' not expressed in octal.\n", data);
794 if (strcmp (data, "dmask") == 0) {
795 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
796 data = "dir_mode";
798 /* the following eight mount options should be
799 stripped out from what is passed into the kernel
800 since these eight options are best passed as the
801 mount flags rather than redundantly to the kernel
802 and could generate spurious warnings depending on the
803 level of the corresponding cifs vfs kernel code */
804 } else if (strncmp(data, "nosuid", 6) == 0) {
805 *filesys_flags |= MS_NOSUID;
806 } else if (strncmp(data, "suid", 4) == 0) {
807 *filesys_flags &= ~MS_NOSUID;
808 } else if (strncmp(data, "nodev", 5) == 0) {
809 *filesys_flags |= MS_NODEV;
810 } else if ((strncmp(data, "nobrl", 5) == 0) ||
811 (strncmp(data, "nolock", 6) == 0)) {
812 *filesys_flags &= ~MS_MANDLOCK;
813 } else if (strncmp(data, "dev", 3) == 0) {
814 *filesys_flags &= ~MS_NODEV;
815 } else if (strncmp(data, "noexec", 6) == 0) {
816 *filesys_flags |= MS_NOEXEC;
817 } else if (strncmp(data, "exec", 4) == 0) {
818 *filesys_flags &= ~MS_NOEXEC;
819 } else if (strncmp(data, "guest", 5) == 0) {
820 user_name = (char *)calloc(1, 1);
821 got_user = 1;
822 got_password = 1;
823 } else if (strncmp(data, "ro", 2) == 0) {
824 *filesys_flags |= MS_RDONLY;
825 } else if (strncmp(data, "rw", 2) == 0) {
826 *filesys_flags &= ~MS_RDONLY;
827 } else if (strncmp(data, "remount", 7) == 0) {
828 *filesys_flags |= MS_REMOUNT;
829 } /* else if (strnicmp(data, "port", 4) == 0) {
830 if (value && *value) {
831 vol->port =
832 simple_strtoul(value, &value, 0);
834 } else if (strnicmp(data, "rsize", 5) == 0) {
835 if (value && *value) {
836 vol->rsize =
837 simple_strtoul(value, &value, 0);
839 } else if (strnicmp(data, "wsize", 5) == 0) {
840 if (value && *value) {
841 vol->wsize =
842 simple_strtoul(value, &value, 0);
844 } else if (strnicmp(data, "version", 3) == 0) {
845 } else {
846 printf("CIFS: Unknown mount option %s\n",data);
847 } */ /* nothing to do on those four mount options above.
848 Just pass to kernel and ignore them here */
850 /* Copy (possibly modified) option to out */
851 word_len = strlen(data);
852 if (value)
853 word_len += 1 + strlen(value);
855 out = (char *)realloc(out, out_len + word_len + 2);
856 if (out == NULL) {
857 perror("malloc");
858 exit(EX_SYSERR);
861 if (out_len) {
862 strlcat(out, ",", out_len + word_len + 2);
863 out_len++;
866 if (value)
867 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
868 else
869 snprintf(out + out_len, word_len + 1, "%s", data);
870 out_len = strlen(out);
872 nocopy:
873 data = next_keyword;
876 /* special-case the uid and gid */
877 if (got_uid) {
878 word_len = strlen(user);
880 out = (char *)realloc(out, out_len + word_len + 6);
881 if (out == NULL) {
882 perror("malloc");
883 exit(EX_SYSERR);
886 if (out_len) {
887 strlcat(out, ",", out_len + word_len + 6);
888 out_len++;
890 snprintf(out + out_len, word_len + 5, "uid=%s", user);
891 out_len = strlen(out);
893 if (got_gid) {
894 word_len = strlen(group);
896 out = (char *)realloc(out, out_len + 1 + word_len + 6);
897 if (out == NULL) {
898 perror("malloc");
899 exit(EX_SYSERR);
902 if (out_len) {
903 strlcat(out, ",", out_len + word_len + 6);
904 out_len++;
906 snprintf(out + out_len, word_len + 5, "gid=%s", group);
907 out_len = strlen(out);
910 SAFE_FREE(*optionsp);
911 *optionsp = out;
912 return 0;
915 /* replace all (one or more) commas with double commas */
916 static void check_for_comma(char ** ppasswrd)
918 char *new_pass_buf;
919 char *pass;
920 int i,j;
921 int number_of_commas = 0;
922 int len;
924 if(ppasswrd == NULL)
925 return;
926 else
927 (pass = *ppasswrd);
929 len = strlen(pass);
931 for(i=0;i<len;i++) {
932 if(pass[i] == ',')
933 number_of_commas++;
936 if(number_of_commas == 0)
937 return;
938 if(number_of_commas > MOUNT_PASSWD_SIZE) {
939 /* would otherwise overflow the mount options buffer */
940 printf("\nInvalid password. Password contains too many commas.\n");
941 return;
944 new_pass_buf = (char *)malloc(len+number_of_commas+1);
945 if(new_pass_buf == NULL)
946 return;
948 for(i=0,j=0;i<len;i++,j++) {
949 new_pass_buf[j] = pass[i];
950 if(pass[i] == ',') {
951 j++;
952 new_pass_buf[j] = pass[i];
955 new_pass_buf[len+number_of_commas] = 0;
957 SAFE_FREE(*ppasswrd);
958 *ppasswrd = new_pass_buf;
960 return;
963 /* Usernames can not have backslash in them and we use
964 [BB check if usernames can have forward slash in them BB]
965 backslash as domain\user separator character
967 static char * check_for_domain(char **ppuser)
969 char * original_string;
970 char * usernm;
971 char * domainnm;
972 int original_len;
973 int len;
974 int i;
976 if(ppuser == NULL)
977 return NULL;
979 original_string = *ppuser;
981 if (original_string == NULL)
982 return NULL;
984 original_len = strlen(original_string);
986 usernm = strchr(*ppuser,'/');
987 if (usernm == NULL) {
988 usernm = strchr(*ppuser,'\\');
989 if (usernm == NULL)
990 return NULL;
993 if(got_domain) {
994 printf("Domain name specified twice. Username probably malformed\n");
995 return NULL;
998 usernm[0] = 0;
999 domainnm = *ppuser;
1000 if (domainnm[0] != 0) {
1001 got_domain = 1;
1002 } else {
1003 printf("null domain\n");
1005 len = strlen(domainnm);
1006 /* reset domainm to new buffer, and copy
1007 domain name into it */
1008 domainnm = (char *)malloc(len+1);
1009 if(domainnm == NULL)
1010 return NULL;
1012 strlcpy(domainnm,*ppuser,len+1);
1014 /* move_string(*ppuser, usernm+1) */
1015 len = strlen(usernm+1);
1017 if(len >= original_len) {
1018 /* should not happen */
1019 return domainnm;
1022 for(i=0;i<original_len;i++) {
1023 if(i<len)
1024 original_string[i] = usernm[i+1];
1025 else /* stuff with commas to remove last parm */
1026 original_string[i] = ',';
1029 /* BB add check for more than one slash?
1030 strchr(*ppuser,'/');
1031 strchr(*ppuser,'\\')
1034 return domainnm;
1037 /* replace all occurances of "from" in a string with "to" */
1038 static void replace_char(char *string, char from, char to, int maxlen)
1040 char *lastchar = string + maxlen;
1041 while (string) {
1042 string = strchr(string, from);
1043 if (string) {
1044 *string = to;
1045 if (string >= lastchar)
1046 return;
1051 /* Note that caller frees the returned buffer if necessary */
1052 static struct addrinfo *
1053 parse_server(char ** punc_name)
1055 char * unc_name = *punc_name;
1056 int length = strnlen(unc_name, MAX_UNC_LEN);
1057 char * share;
1058 struct addrinfo *addrlist;
1059 int rc;
1061 if(length > (MAX_UNC_LEN - 1)) {
1062 printf("mount error: UNC name too long");
1063 return NULL;
1065 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1066 (strncasecmp("smb://", unc_name, 6) == 0)) {
1067 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
1068 return NULL;
1071 if(length < 3) {
1072 /* BB add code to find DFS root here */
1073 printf("\nMounting the DFS root for domain not implemented yet\n");
1074 return NULL;
1075 } else {
1076 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
1077 /* check for nfs syntax ie server:share */
1078 share = strchr(unc_name,':');
1079 if(share) {
1080 *punc_name = (char *)malloc(length+3);
1081 if(*punc_name == NULL) {
1082 /* put the original string back if
1083 no memory left */
1084 *punc_name = unc_name;
1085 return NULL;
1087 *share = '/';
1088 strlcpy((*punc_name)+2,unc_name,length+1);
1089 SAFE_FREE(unc_name);
1090 unc_name = *punc_name;
1091 unc_name[length+2] = 0;
1092 goto continue_unc_parsing;
1093 } else {
1094 printf("mount error: improperly formatted UNC name.");
1095 printf(" %s does not begin with \\\\ or //\n",unc_name);
1096 return NULL;
1098 } else {
1099 continue_unc_parsing:
1100 unc_name[0] = '/';
1101 unc_name[1] = '/';
1102 unc_name += 2;
1104 /* allow for either delimiter between host and sharename */
1105 if ((share = strpbrk(unc_name, "/\\"))) {
1106 *share = 0; /* temporarily terminate the string */
1107 share += 1;
1108 if(got_ip == 0) {
1109 rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
1110 if (rc != 0) {
1111 printf("mount error: could not resolve address for %s: %s\n",
1112 unc_name, gai_strerror(rc));
1113 addrlist = NULL;
1116 *(share - 1) = '/'; /* put delimiter back */
1118 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1119 if ((prefixpath = strpbrk(share, "/\\"))) {
1120 *prefixpath = 0; /* permanently terminate the string */
1121 if (!strlen(++prefixpath))
1122 prefixpath = NULL; /* this needs to be done explicitly */
1124 if(got_ip) {
1125 if(verboseflag)
1126 printf("ip address specified explicitly\n");
1127 return NULL;
1129 /* BB should we pass an alternate version of the share name as Unicode */
1131 return addrlist;
1132 } else {
1133 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1134 printf("Mounting the DFS root for a particular server not implemented yet\n");
1135 return NULL;
1141 static struct option longopts[] = {
1142 { "all", 0, NULL, 'a' },
1143 { "help",0, NULL, 'h' },
1144 { "move",0, NULL, 'm' },
1145 { "bind",0, NULL, 'b' },
1146 { "read-only", 0, NULL, 'r' },
1147 { "ro", 0, NULL, 'r' },
1148 { "verbose", 0, NULL, 'v' },
1149 { "version", 0, NULL, 'V' },
1150 { "read-write", 0, NULL, 'w' },
1151 { "rw", 0, NULL, 'w' },
1152 { "options", 1, NULL, 'o' },
1153 { "type", 1, NULL, 't' },
1154 { "rsize",1, NULL, 'R' },
1155 { "wsize",1, NULL, 'W' },
1156 { "uid", 1, NULL, '1'},
1157 { "gid", 1, NULL, '2'},
1158 { "user",1,NULL,'u'},
1159 { "username",1,NULL,'u'},
1160 { "dom",1,NULL,'d'},
1161 { "domain",1,NULL,'d'},
1162 { "password",1,NULL,'p'},
1163 { "pass",1,NULL,'p'},
1164 { "credentials",1,NULL,'c'},
1165 { "port",1,NULL,'P'},
1166 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1167 { NULL, 0, NULL, 0 }
1170 /* convert a string to uppercase. return false if the string
1171 * wasn't ASCII. Return success on a NULL ptr */
1172 static int
1173 uppercase_string(char *string)
1175 if (!string)
1176 return 1;
1178 while (*string) {
1179 /* check for unicode */
1180 if ((unsigned char) string[0] & 0x80)
1181 return 0;
1182 *string = toupper((unsigned char) *string);
1183 string++;
1186 return 1;
1189 static void print_cifs_mount_version(void)
1191 printf("mount.cifs version: %s.%s%s\n",
1192 MOUNT_CIFS_VERSION_MAJOR,
1193 MOUNT_CIFS_VERSION_MINOR,
1194 MOUNT_CIFS_VENDOR_SUFFIX);
1198 * This function borrowed from fuse-utils...
1200 * glibc's addmntent (at least as of 2.10 or so) doesn't properly encode
1201 * newlines embedded within the text fields. To make sure no one corrupts
1202 * the mtab, fail the mount if there are embedded newlines.
1204 static int check_newline(const char *progname, const char *name)
1206 char *s;
1207 for (s = "\n"; *s; s++) {
1208 if (strchr(name, *s)) {
1209 fprintf(stderr, "%s: illegal character 0x%02x in mount entry\n",
1210 progname, *s);
1211 return EX_USAGE;
1214 return 0;
1217 static int check_mtab(const char *progname, const char *devname,
1218 const char *dir)
1220 if (check_newline(progname, devname) == -1 ||
1221 check_newline(progname, dir) == -1)
1222 return EX_USAGE;
1223 return 0;
1227 int main(int argc, char ** argv)
1229 int c;
1230 unsigned long flags = MS_MANDLOCK;
1231 char * orgoptions = NULL;
1232 char * share_name = NULL;
1233 const char * ipaddr = NULL;
1234 char * uuid = NULL;
1235 char * mountpoint = NULL;
1236 char * options = NULL;
1237 char * optionstail;
1238 char * resolved_path = NULL;
1239 char * temp;
1240 char * dev_name;
1241 int rc = 0;
1242 int rsize = 0;
1243 int wsize = 0;
1244 int nomtab = 0;
1245 int uid = 0;
1246 int gid = 0;
1247 int optlen = 0;
1248 int orgoptlen = 0;
1249 size_t options_size = 0;
1250 size_t current_len;
1251 int retry = 0; /* set when we have to retry mount with uppercase */
1252 struct addrinfo *addrhead = NULL, *addr;
1253 struct utsname sysinfo;
1254 struct mntent mountent;
1255 struct sockaddr_in *addr4;
1256 struct sockaddr_in6 *addr6;
1257 FILE * pmntfile;
1259 if (check_setuid())
1260 return EX_USAGE;
1262 /* setlocale(LC_ALL, "");
1263 bindtextdomain(PACKAGE, LOCALEDIR);
1264 textdomain(PACKAGE); */
1266 if(argc && argv) {
1267 thisprogram = argv[0];
1268 } else {
1269 mount_cifs_usage();
1270 exit(EX_USAGE);
1273 if(thisprogram == NULL)
1274 thisprogram = "mount.cifs";
1276 uname(&sysinfo);
1277 /* BB add workstation name and domain and pass down */
1279 /* #ifdef _GNU_SOURCE
1280 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1281 #endif */
1282 if(argc > 2) {
1283 dev_name = argv[1];
1284 share_name = strndup(argv[1], MAX_UNC_LEN);
1285 if (share_name == NULL) {
1286 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1287 exit(EX_SYSERR);
1289 mountpoint = argv[2];
1290 } else if (argc == 2) {
1291 if ((strcmp(argv[1], "-V") == 0) ||
1292 (strcmp(argv[1], "--version") == 0))
1294 print_cifs_mount_version();
1295 exit(0);
1298 if ((strcmp(argv[1], "-h") == 0) ||
1299 (strcmp(argv[1], "-?") == 0) ||
1300 (strcmp(argv[1], "--help") == 0))
1302 mount_cifs_usage();
1303 exit(0);
1306 mount_cifs_usage();
1307 exit(EX_USAGE);
1308 } else {
1309 mount_cifs_usage();
1310 exit(EX_USAGE);
1314 /* add sharename in opts string as unc= parm */
1315 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1316 longopts, NULL)) != -1) {
1317 switch (c) {
1318 /* No code to do the following options yet */
1319 /* case 'l':
1320 list_with_volumelabel = 1;
1321 break;
1322 case 'L':
1323 volumelabel = optarg;
1324 break; */
1325 /* case 'a':
1326 ++mount_all;
1327 break; */
1329 case '?':
1330 case 'h': /* help */
1331 mount_cifs_usage ();
1332 exit(0);
1333 case 'n':
1334 ++nomtab;
1335 break;
1336 case 'b':
1337 #ifdef MS_BIND
1338 flags |= MS_BIND;
1339 #else
1340 fprintf(stderr,
1341 "option 'b' (MS_BIND) not supported\n");
1342 #endif
1343 break;
1344 case 'm':
1345 #ifdef MS_MOVE
1346 flags |= MS_MOVE;
1347 #else
1348 fprintf(stderr,
1349 "option 'm' (MS_MOVE) not supported\n");
1350 #endif
1351 break;
1352 case 'o':
1353 orgoptions = strdup(optarg);
1354 break;
1355 case 'r': /* mount readonly */
1356 flags |= MS_RDONLY;
1357 break;
1358 case 'U':
1359 uuid = optarg;
1360 break;
1361 case 'v':
1362 ++verboseflag;
1363 break;
1364 case 'V':
1365 print_cifs_mount_version();
1366 exit (0);
1367 case 'w':
1368 flags &= ~MS_RDONLY;
1369 break;
1370 case 'R':
1371 rsize = atoi(optarg) ;
1372 break;
1373 case 'W':
1374 wsize = atoi(optarg);
1375 break;
1376 case '1':
1377 if (isdigit(*optarg)) {
1378 char *ep;
1380 uid = strtoul(optarg, &ep, 10);
1381 if (*ep) {
1382 printf("bad uid value \"%s\"\n", optarg);
1383 exit(EX_USAGE);
1385 } else {
1386 struct passwd *pw;
1388 if (!(pw = getpwnam(optarg))) {
1389 printf("bad user name \"%s\"\n", optarg);
1390 exit(EX_USAGE);
1392 uid = pw->pw_uid;
1393 endpwent();
1395 break;
1396 case '2':
1397 if (isdigit(*optarg)) {
1398 char *ep;
1400 gid = strtoul(optarg, &ep, 10);
1401 if (*ep) {
1402 printf("bad gid value \"%s\"\n", optarg);
1403 exit(EX_USAGE);
1405 } else {
1406 struct group *gr;
1408 if (!(gr = getgrnam(optarg))) {
1409 printf("bad user name \"%s\"\n", optarg);
1410 exit(EX_USAGE);
1412 gid = gr->gr_gid;
1413 endpwent();
1415 break;
1416 case 'u':
1417 got_user = 1;
1418 user_name = optarg;
1419 break;
1420 case 'd':
1421 domain_name = optarg; /* BB fix this - currently ignored */
1422 got_domain = 1;
1423 break;
1424 case 'p':
1425 if(mountpassword == NULL)
1426 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1427 if(mountpassword) {
1428 got_password = 1;
1429 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1431 break;
1432 case 'S':
1433 get_password_from_file(0 /* stdin */,NULL);
1434 break;
1435 case 't':
1436 break;
1437 case 'f':
1438 ++fakemnt;
1439 break;
1440 default:
1441 printf("unknown mount option %c\n",c);
1442 mount_cifs_usage();
1443 exit(EX_USAGE);
1447 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1448 mount_cifs_usage();
1449 exit(EX_USAGE);
1452 /* make sure mountpoint is legit */
1453 rc = chdir(mountpoint);
1454 if (rc) {
1455 fprintf(stderr, "Couldn't chdir to %s: %s\n", mountpoint,
1456 strerror(errno));
1457 rc = EX_USAGE;
1458 goto mount_exit;
1461 rc = check_mountpoint(thisprogram, mountpoint);
1462 if (rc)
1463 goto mount_exit;
1465 /* sanity check for unprivileged mounts */
1466 if (getuid()) {
1467 rc = check_fstab(thisprogram, mountpoint, dev_name,
1468 &orgoptions);
1469 if (rc)
1470 goto mount_exit;
1472 /* enable any default user mount flags */
1473 flags |= CIFS_SETUID_FLAGS;
1476 if (getenv("PASSWD")) {
1477 if(mountpassword == NULL)
1478 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1479 if(mountpassword) {
1480 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1481 got_password = 1;
1483 } else if (getenv("PASSWD_FD")) {
1484 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1485 } else if (getenv("PASSWD_FILE")) {
1486 get_password_from_file(0, getenv("PASSWD_FILE"));
1489 if (orgoptions && parse_options(&orgoptions, &flags)) {
1490 rc = EX_USAGE;
1491 goto mount_exit;
1494 if (getuid()) {
1495 #if !CIFS_LEGACY_SETUID_CHECK
1496 if (!(flags & (MS_USERS|MS_USER))) {
1497 fprintf(stderr, "%s: permission denied\n", thisprogram);
1498 rc = EX_USAGE;
1499 goto mount_exit;
1501 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1503 if (geteuid()) {
1504 fprintf(stderr, "%s: not installed setuid - \"user\" "
1505 "CIFS mounts not supported.",
1506 thisprogram);
1507 rc = EX_FAIL;
1508 goto mount_exit;
1512 flags &= ~(MS_USERS|MS_USER);
1514 addrhead = addr = parse_server(&share_name);
1515 if((addrhead == NULL) && (got_ip == 0)) {
1516 printf("No ip address specified and hostname not found\n");
1517 rc = EX_USAGE;
1518 goto mount_exit;
1521 /* BB save off path and pop after mount returns? */
1522 resolved_path = (char *)malloc(PATH_MAX+1);
1523 if (!resolved_path) {
1524 fprintf(stderr, "Unable to allocate memory.\n");
1525 rc = EX_SYSERR;
1526 goto mount_exit;
1529 /* Note that if we can not canonicalize the name, we get
1530 another chance to see if it is valid when we chdir to it */
1531 if(!realpath(".", resolved_path)) {
1532 fprintf(stderr, "Unable to resolve %s to canonical path: %s\n",
1533 mountpoint, strerror(errno));
1534 rc = EX_SYSERR;
1535 goto mount_exit;
1538 mountpoint = resolved_path;
1540 if(got_user == 0) {
1541 /* Note that the password will not be retrieved from the
1542 USER env variable (ie user%password form) as there is
1543 already a PASSWD environment varaible */
1544 if (getenv("USER"))
1545 user_name = strdup(getenv("USER"));
1546 if (user_name == NULL)
1547 user_name = getusername();
1548 got_user = 1;
1551 if(got_password == 0) {
1552 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1553 no good replacement yet. */
1554 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1555 if (!tmp_pass || !mountpassword) {
1556 printf("Password not entered, exiting\n");
1557 exit(EX_USAGE);
1559 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1560 got_password = 1;
1562 /* FIXME launch daemon (handles dfs name resolution and credential change)
1563 remember to clear parms and overwrite password field before launching */
1564 if(orgoptions) {
1565 optlen = strlen(orgoptions);
1566 orgoptlen = optlen;
1567 } else
1568 optlen = 0;
1569 if(share_name)
1570 optlen += strlen(share_name) + 4;
1571 else {
1572 printf("No server share name specified\n");
1573 printf("\nMounting the DFS root for server not implemented yet\n");
1574 exit(EX_USAGE);
1576 if(user_name)
1577 optlen += strlen(user_name) + 6;
1578 optlen += MAX_ADDRESS_LEN + 4;
1579 if(mountpassword)
1580 optlen += strlen(mountpassword) + 6;
1581 mount_retry:
1582 SAFE_FREE(options);
1583 options_size = optlen + 10 + DOMAIN_SIZE;
1584 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 */);
1586 if(options == NULL) {
1587 printf("Could not allocate memory for mount options\n");
1588 exit(EX_SYSERR);
1591 strlcpy(options, "unc=", options_size);
1592 strlcat(options,share_name,options_size);
1593 /* scan backwards and reverse direction of slash */
1594 temp = strrchr(options, '/');
1595 if(temp > options + 6)
1596 *temp = '\\';
1597 if(user_name) {
1598 /* check for syntax like user=domain\user */
1599 if(got_domain == 0)
1600 domain_name = check_for_domain(&user_name);
1601 strlcat(options,",user=",options_size);
1602 strlcat(options,user_name,options_size);
1604 if(retry == 0) {
1605 if(domain_name) {
1606 /* extra length accounted for in option string above */
1607 strlcat(options,",domain=",options_size);
1608 strlcat(options,domain_name,options_size);
1612 strlcat(options,",ver=",options_size);
1613 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1615 if(orgoptions) {
1616 strlcat(options,",",options_size);
1617 strlcat(options,orgoptions,options_size);
1619 if(prefixpath) {
1620 strlcat(options,",prefixpath=",options_size);
1621 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1624 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1625 replace_char(dev_name, '\\', '/', strlen(share_name));
1627 if (!got_ip && addr) {
1628 strlcat(options, ",ip=", options_size);
1629 current_len = strnlen(options, options_size);
1630 optionstail = options + current_len;
1631 switch (addr->ai_addr->sa_family) {
1632 case AF_INET6:
1633 addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1634 ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1635 options_size - current_len);
1636 break;
1637 case AF_INET:
1638 addr4 = (struct sockaddr_in *) addr->ai_addr;
1639 ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1640 options_size - current_len);
1641 break;
1644 /* if the address looks bogus, try the next one */
1645 if (!ipaddr) {
1646 addr = addr->ai_next;
1647 if (addr)
1648 goto mount_retry;
1649 rc = EX_SYSERR;
1650 goto mount_exit;
1654 if(verboseflag)
1655 fprintf(stderr, "\nmount.cifs kernel mount options: %s", options);
1657 if (mountpassword) {
1659 * Commas have to be doubled, or else they will
1660 * look like the parameter separator
1662 if(retry == 0)
1663 check_for_comma(&mountpassword);
1664 strlcat(options,",pass=",options_size);
1665 strlcat(options,mountpassword,options_size);
1666 if (verboseflag)
1667 fprintf(stderr, ",pass=********");
1670 if (verboseflag)
1671 fprintf(stderr, "\n");
1673 rc = check_mtab(thisprogram, dev_name, mountpoint);
1674 if (rc)
1675 goto mount_exit;
1677 if (!fakemnt && mount(dev_name, ".", "cifs", flags, options)) {
1678 switch (errno) {
1679 case ECONNREFUSED:
1680 case EHOSTUNREACH:
1681 if (addr) {
1682 addr = addr->ai_next;
1683 if (addr)
1684 goto mount_retry;
1686 break;
1687 case ENODEV:
1688 printf("mount error: cifs filesystem not supported by the system\n");
1689 break;
1690 case ENXIO:
1691 if(retry == 0) {
1692 retry = 1;
1693 if (uppercase_string(dev_name) &&
1694 uppercase_string(share_name) &&
1695 uppercase_string(prefixpath)) {
1696 printf("retrying with upper case share name\n");
1697 goto mount_retry;
1701 printf("mount error(%d): %s\n", errno, strerror(errno));
1702 printf("Refer to the mount.cifs(8) manual page (e.g. man "
1703 "mount.cifs)\n");
1704 rc = EX_FAIL;
1705 goto mount_exit;
1708 if (nomtab)
1709 goto mount_exit;
1710 atexit(unlock_mtab);
1711 rc = lock_mtab();
1712 if (rc) {
1713 printf("cannot lock mtab");
1714 goto mount_exit;
1716 pmntfile = setmntent(MOUNTED, "a+");
1717 if (!pmntfile) {
1718 printf("could not update mount table\n");
1719 unlock_mtab();
1720 rc = EX_FILEIO;
1721 goto mount_exit;
1723 mountent.mnt_fsname = dev_name;
1724 mountent.mnt_dir = mountpoint;
1725 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1726 mountent.mnt_opts = (char *)malloc(220);
1727 if(mountent.mnt_opts) {
1728 char * mount_user = getusername();
1729 memset(mountent.mnt_opts,0,200);
1730 if(flags & MS_RDONLY)
1731 strlcat(mountent.mnt_opts,"ro",220);
1732 else
1733 strlcat(mountent.mnt_opts,"rw",220);
1734 if(flags & MS_MANDLOCK)
1735 strlcat(mountent.mnt_opts,",mand",220);
1736 if(flags & MS_NOEXEC)
1737 strlcat(mountent.mnt_opts,",noexec",220);
1738 if(flags & MS_NOSUID)
1739 strlcat(mountent.mnt_opts,",nosuid",220);
1740 if(flags & MS_NODEV)
1741 strlcat(mountent.mnt_opts,",nodev",220);
1742 if(flags & MS_SYNCHRONOUS)
1743 strlcat(mountent.mnt_opts,",sync",220);
1744 if(mount_user) {
1745 if(getuid() != 0) {
1746 strlcat(mountent.mnt_opts,
1747 ",user=", 220);
1748 strlcat(mountent.mnt_opts,
1749 mount_user, 220);
1753 mountent.mnt_freq = 0;
1754 mountent.mnt_passno = 0;
1755 rc = addmntent(pmntfile,&mountent);
1756 endmntent(pmntfile);
1757 unlock_mtab();
1758 SAFE_FREE(mountent.mnt_opts);
1759 if (rc)
1760 rc = EX_FILEIO;
1761 mount_exit:
1762 if(mountpassword) {
1763 int len = strlen(mountpassword);
1764 memset(mountpassword,0,len);
1765 SAFE_FREE(mountpassword);
1768 if (addrhead)
1769 freeaddrinfo(addrhead);
1770 SAFE_FREE(options);
1771 SAFE_FREE(orgoptions);
1772 SAFE_FREE(resolved_path);
1773 SAFE_FREE(share_name);
1774 exit(rc);