mount.cifs: check access of credential files before opening
[Samba.git] / client / mount.cifs.c
blob0f41afff9cc6d4c59a30646dd8cfe4e6529f7d64
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 "13"
48 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
49 #ifdef _SAMBA_BUILD_
50 #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 128
86 #define DOMAIN_SIZE 64
88 /* currently maximum length of IPv6 address string */
89 #define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
92 * By default, mount.cifs follows the conventions set forth by /bin/mount
93 * for user mounts. That is, it requires that the mount be listed in
94 * /etc/fstab with the "user" option when run as an unprivileged user and
95 * mount.cifs is setuid root.
97 * Older versions of mount.cifs however were "looser" in this regard. When
98 * made setuid root, a user could run mount.cifs directly and mount any share
99 * on a directory owned by that user.
101 * The legacy behavior is now disabled by default. To reenable it, set the
102 * following #define to true.
104 #define CIFS_LEGACY_SETUID_CHECK 0
107 * When an unprivileged user runs a setuid mount.cifs, we set certain mount
108 * flags by default. These defaults can be changed here.
110 #define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
112 const char *thisprogram;
113 int verboseflag = 0;
114 int fakemnt = 0;
115 static int got_password = 0;
116 static int got_user = 0;
117 static int got_domain = 0;
118 static int got_ip = 0;
119 static int got_unc = 0;
120 static int got_uid = 0;
121 static int got_gid = 0;
122 static char * user_name = NULL;
123 static char * mountpassword = NULL;
124 char * domain_name = NULL;
125 char * prefixpath = NULL;
127 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
128 * don't link to libreplace so need them here. */
130 /* like strncpy but does not 0 fill the buffer and always null
131 * terminates. bufsize is the size of the destination buffer */
133 #ifndef HAVE_STRLCPY
134 static size_t strlcpy(char *d, const char *s, size_t bufsize)
136 size_t len = strlen(s);
137 size_t ret = len;
138 if (bufsize <= 0) return 0;
139 if (len >= bufsize) len = bufsize-1;
140 memcpy(d, s, len);
141 d[len] = 0;
142 return ret;
144 #endif
146 /* like strncat but does not 0 fill the buffer and always null
147 * terminates. bufsize is the length of the buffer, which should
148 * be one more than the maximum resulting string length */
150 #ifndef HAVE_STRLCAT
151 static size_t strlcat(char *d, const char *s, size_t bufsize)
153 size_t len1 = strlen(d);
154 size_t len2 = strlen(s);
155 size_t ret = len1 + len2;
157 if (len1+len2 >= bufsize) {
158 if (bufsize < (len1+1)) {
159 return ret;
161 len2 = bufsize - (len1+1);
163 if (len2 > 0) {
164 memcpy(d+len1, s, len2);
165 d[len1+len2] = 0;
167 return ret;
169 #endif
172 * If an unprivileged user is doing the mounting then we need to ensure
173 * that the entry is in /etc/fstab.
175 static int
176 check_mountpoint(const char *progname, char *mountpoint)
178 int err;
179 struct stat statbuf;
181 /* does mountpoint exist and is it a directory? */
182 err = stat(mountpoint, &statbuf);
183 if (err) {
184 fprintf(stderr, "%s: failed to stat %s: %s\n", progname,
185 mountpoint, strerror(errno));
186 return EX_USAGE;
189 if (!S_ISDIR(statbuf.st_mode)) {
190 fprintf(stderr, "%s: %s is not a directory!", progname,
191 mountpoint);
192 return EX_USAGE;
195 #if CIFS_LEGACY_SETUID_CHECK
196 /* do extra checks on mountpoint for legacy setuid behavior */
197 if (!getuid() || geteuid())
198 return 0;
200 if (statbuf.st_uid != getuid()) {
201 fprintf(stderr, "%s: %s is not owned by user\n", progname,
202 mountpoint);
203 return EX_USAGE;
206 if ((statbuf.st_mode & S_IRWXU) != S_IRWXU) {
207 fprintf(stderr, "%s: invalid permissions on %s\n", progname,
208 mountpoint);
209 return EX_USAGE;
211 #endif /* CIFS_LEGACY_SETUID_CHECK */
213 return 0;
216 #if CIFS_LEGACY_SETUID_CHECK
217 static int
218 check_fstab(const char *progname, char *mountpoint, char *devname,
219 char **options)
221 return 0;
223 #else /* CIFS_LEGACY_SETUID_CHECK */
224 static int
225 check_fstab(const char *progname, char *mountpoint, char *devname,
226 char **options)
228 FILE *fstab;
229 struct mntent *mnt;
231 /* make sure this mount is listed in /etc/fstab */
232 fstab = setmntent(_PATH_FSTAB, "r");
233 if (!fstab) {
234 fprintf(stderr, "Couldn't open %s for reading!\n",
235 _PATH_FSTAB);
236 return EX_FILEIO;
239 while((mnt = getmntent(fstab))) {
240 if (!strcmp(mountpoint, mnt->mnt_dir))
241 break;
243 endmntent(fstab);
245 if (mnt == NULL || strcmp(mnt->mnt_fsname, devname)) {
246 fprintf(stderr, "%s: permission denied: no match for "
247 "%s found in %s\n", progname, mountpoint,
248 _PATH_FSTAB);
249 return EX_USAGE;
253 * 'mount' munges the options from fstab before passing them
254 * to us. It is non-trivial to test that we have the correct
255 * set of options. We don't want to trust what the user
256 * gave us, so just take whatever is in /etc/fstab.
258 free(*options);
259 *options = strdup(mnt->mnt_opts);
260 return 0;
262 #endif /* CIFS_LEGACY_SETUID_CHECK */
264 /* BB finish BB
266 cifs_umount
267 open nofollow - avoid symlink exposure?
268 get owner of dir see if matches self or if root
269 call system(umount argv) etc.
271 BB end finish BB */
273 static char * check_for_domain(char **);
276 static void mount_cifs_usage(FILE *stream)
278 fprintf(stream, "\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
279 fprintf(stream, "\nMount the remote target, specified as a UNC name,");
280 fprintf(stream, " to a local directory.\n\nOptions:\n");
281 fprintf(stream, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
282 fprintf(stream, "\nLess commonly used options:");
283 fprintf(stream, "\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
284 fprintf(stream, "\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
285 fprintf(stream, "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
286 fprintf(stream, "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
287 fprintf(stream, "\n\nOptions not needed for servers supporting CIFS Unix extensions");
288 fprintf(stream, "\n\t(e.g. unneeded for mounts to most Samba versions):");
289 fprintf(stream, "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
290 fprintf(stream, "\n\nRarely used options:");
291 fprintf(stream, "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
292 fprintf(stream, "\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
293 fprintf(stream, "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
294 fprintf(stream, "\n\nOptions are described in more detail in the manual page");
295 fprintf(stream, "\n\tman 8 mount.cifs\n");
296 fprintf(stream, "\nTo display the version number of the mount helper:");
297 fprintf(stream, "\n\t%s -V\n",thisprogram);
299 SAFE_FREE(mountpassword);
301 if (stream == stderr)
302 exit(EX_USAGE);
303 exit(0);
306 /* caller frees username if necessary */
307 static char * getusername(void) {
308 char *username = NULL;
309 struct passwd *password = getpwuid(getuid());
311 if (password) {
312 username = password->pw_name;
314 return username;
317 static int open_cred_file(char * file_name)
319 char * line_buf;
320 char * temp_val;
321 FILE * fs;
322 int i, length;
324 i = access(file_name, R_OK);
325 if (i)
326 return i;
328 fs = fopen(file_name,"r");
329 if(fs == NULL)
330 return errno;
331 line_buf = (char *)malloc(4096);
332 if(line_buf == NULL) {
333 fclose(fs);
334 return ENOMEM;
337 while(fgets(line_buf,4096,fs)) {
338 /* parse line from credential file */
340 /* eat leading white space */
341 for(i=0;i<4086;i++) {
342 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
343 break;
344 /* if whitespace - skip past it */
346 if (strncasecmp("username",line_buf+i,8) == 0) {
347 temp_val = strchr(line_buf + i,'=');
348 if(temp_val) {
349 /* go past equals sign */
350 temp_val++;
351 for(length = 0;length<4087;length++) {
352 if ((temp_val[length] == '\n')
353 || (temp_val[length] == '\0')) {
354 temp_val[length] = '\0';
355 break;
358 if(length > 4086) {
359 fprintf(stderr, "mount.cifs failed due to malformed username in credentials file\n");
360 memset(line_buf,0,4096);
361 exit(EX_USAGE);
362 } else {
363 got_user = 1;
364 user_name = (char *)calloc(1 + length,1);
365 /* BB adding free of user_name string before exit,
366 not really necessary but would be cleaner */
367 strlcpy(user_name,temp_val, length+1);
370 } else if (strncasecmp("password",line_buf+i,8) == 0) {
371 temp_val = strchr(line_buf+i,'=');
372 if(temp_val) {
373 /* go past equals sign */
374 temp_val++;
375 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
376 if ((temp_val[length] == '\n')
377 || (temp_val[length] == '\0')) {
378 temp_val[length] = '\0';
379 break;
382 if(length > MOUNT_PASSWD_SIZE) {
383 fprintf(stderr, "mount.cifs failed: password in credentials file too long\n");
384 memset(line_buf,0, 4096);
385 exit(EX_USAGE);
386 } else {
387 if(mountpassword == NULL) {
388 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
389 } else
390 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
391 if(mountpassword) {
392 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
393 got_password = 1;
397 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
398 temp_val = strchr(line_buf+i,'=');
399 if(temp_val) {
400 /* go past equals sign */
401 temp_val++;
402 if(verboseflag)
403 fprintf(stderr, "\nDomain %s\n",temp_val);
404 for(length = 0;length<DOMAIN_SIZE+1;length++) {
405 if ((temp_val[length] == '\n')
406 || (temp_val[length] == '\0')) {
407 temp_val[length] = '\0';
408 break;
411 if(length > DOMAIN_SIZE) {
412 fprintf(stderr, "mount.cifs failed: domain in credentials file too long\n");
413 exit(EX_USAGE);
414 } else {
415 if(domain_name == NULL) {
416 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
417 } else
418 memset(domain_name,0,DOMAIN_SIZE);
419 if(domain_name) {
420 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
421 got_domain = 1;
428 fclose(fs);
429 SAFE_FREE(line_buf);
430 return 0;
433 static int get_password_from_file(int file_descript, char * filename)
435 int rc = 0;
436 int i;
437 char c;
439 if(mountpassword == NULL)
440 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
441 else
442 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
444 if (mountpassword == NULL) {
445 fprintf(stderr, "malloc failed\n");
446 exit(EX_SYSERR);
449 if(filename != NULL) {
450 rc = access(filename, R_OK);
451 if (rc) {
452 fprintf(stderr, "mount.cifs failed: access check of %s failed: %s\n",
453 filename, strerror(errno));
454 exit(EX_SYSERR);
456 file_descript = open(filename, O_RDONLY);
457 if(file_descript < 0) {
458 fprintf(stderr, "mount.cifs failed. %s attempting to open password file %s\n",
459 strerror(errno),filename);
460 exit(EX_SYSERR);
463 /* else file already open and fd provided */
465 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
466 rc = read(file_descript,&c,1);
467 if(rc < 0) {
468 fprintf(stderr, "mount.cifs failed. Error %s reading password file\n",strerror(errno));
469 if(filename != NULL)
470 close(file_descript);
471 exit(EX_SYSERR);
472 } else if(rc == 0) {
473 if(mountpassword[0] == 0) {
474 if(verboseflag)
475 fprintf(stderr, "\nWarning: null password used since cifs password file empty");
477 break;
478 } else /* read valid character */ {
479 if((c == 0) || (c == '\n')) {
480 mountpassword[i] = '\0';
481 break;
482 } else
483 mountpassword[i] = c;
486 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
487 fprintf(stderr, "\nWarning: password longer than %d characters specified in cifs password file",
488 MOUNT_PASSWD_SIZE);
490 got_password = 1;
491 if(filename != NULL) {
492 close(file_descript);
495 return rc;
498 static int parse_options(char ** optionsp, unsigned long * filesys_flags)
500 const char * data;
501 char * percent_char = NULL;
502 char * value = NULL;
503 char * next_keyword = NULL;
504 char * out = NULL;
505 int out_len = 0;
506 int word_len;
507 int rc = 0;
508 char user[32];
509 char group[32];
511 if (!optionsp || !*optionsp)
512 return 1;
513 data = *optionsp;
515 if(verboseflag)
516 fprintf(stderr, "parsing options: %s\n", data);
518 /* BB fixme check for separator override BB */
520 if (getuid()) {
521 got_uid = 1;
522 snprintf(user,sizeof(user),"%u",getuid());
523 got_gid = 1;
524 snprintf(group,sizeof(group),"%u",getgid());
527 /* while ((data = strsep(&options, ",")) != NULL) { */
528 while(data != NULL) {
529 /* check if ends with trailing comma */
530 if(*data == 0)
531 break;
533 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
534 /* data = next keyword */
535 /* value = next value ie stuff after equal sign */
537 next_keyword = strchr(data,','); /* BB handle sep= */
539 /* temporarily null terminate end of keyword=value pair */
540 if(next_keyword)
541 *next_keyword++ = 0;
543 /* temporarily null terminate keyword to make keyword and value distinct */
544 if ((value = strchr(data, '=')) != NULL) {
545 *value = '\0';
546 value++;
549 if (strncmp(data, "users",5) == 0) {
550 if(!value || !*value) {
551 *filesys_flags |= MS_USERS;
552 goto nocopy;
554 } else if (strncmp(data, "user_xattr",10) == 0) {
555 /* do nothing - need to skip so not parsed as user name */
556 } else if (strncmp(data, "user", 4) == 0) {
558 if (!value || !*value) {
559 if(data[4] == '\0') {
560 *filesys_flags |= MS_USER;
561 goto nocopy;
562 } else {
563 fprintf(stderr, "username specified with no parameter\n");
564 SAFE_FREE(out);
565 return 1; /* needs_arg; */
567 } else {
568 if (strnlen(value, 260) < 260) {
569 got_user=1;
570 percent_char = strchr(value,'%');
571 if(percent_char) {
572 *percent_char = ',';
573 if(mountpassword == NULL)
574 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
575 if(mountpassword) {
576 if(got_password)
577 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
578 got_password = 1;
579 percent_char++;
580 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
581 /* remove password from username */
582 while(*percent_char != 0) {
583 *percent_char = ',';
584 percent_char++;
588 /* this is only case in which the user
589 name buf is not malloc - so we have to
590 check for domain name embedded within
591 the user name here since the later
592 call to check_for_domain will not be
593 invoked */
594 domain_name = check_for_domain(&value);
595 } else {
596 fprintf(stderr, "username too long\n");
597 SAFE_FREE(out);
598 return 1;
601 } else if (strncmp(data, "pass", 4) == 0) {
602 if (!value || !*value) {
603 if(got_password) {
604 fprintf(stderr, "\npassword specified twice, ignoring second\n");
605 } else
606 got_password = 1;
607 } else if (strnlen(value, MOUNT_PASSWD_SIZE) < MOUNT_PASSWD_SIZE) {
608 if(got_password)
609 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
610 got_password = 1;
611 } else {
612 fprintf(stderr, "password too long\n");
613 SAFE_FREE(out);
614 return 1;
616 } else if (strncmp(data, "sec", 3) == 0) {
617 if (value) {
618 if (!strncmp(value, "none", 4) ||
619 !strncmp(value, "krb5", 4))
620 got_password = 1;
622 } else if (strncmp(data, "ip", 2) == 0) {
623 if (!value || !*value) {
624 fprintf(stderr, "target ip address argument missing");
625 } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
626 if(verboseflag)
627 fprintf(stderr, "ip address %s override specified\n",value);
628 got_ip = 1;
629 } else {
630 fprintf(stderr, "ip address too long\n");
631 SAFE_FREE(out);
632 return 1;
634 } else if ((strncmp(data, "unc", 3) == 0)
635 || (strncmp(data, "target", 6) == 0)
636 || (strncmp(data, "path", 4) == 0)) {
637 if (!value || !*value) {
638 fprintf(stderr, "invalid path to network resource\n");
639 SAFE_FREE(out);
640 return 1; /* needs_arg; */
641 } else if(strnlen(value,5) < 5) {
642 fprintf(stderr, "UNC name too short");
645 if (strnlen(value, 300) < 300) {
646 got_unc = 1;
647 if (strncmp(value, "//", 2) == 0) {
648 if(got_unc)
649 fprintf(stderr, "unc name specified twice, ignoring second\n");
650 else
651 got_unc = 1;
652 } else if (strncmp(value, "\\\\", 2) != 0) {
653 fprintf(stderr, "UNC Path does not begin with // or \\\\ \n");
654 SAFE_FREE(out);
655 return 1;
656 } else {
657 if(got_unc)
658 fprintf(stderr, "unc name specified twice, ignoring second\n");
659 else
660 got_unc = 1;
662 } else {
663 fprintf(stderr, "CIFS: UNC name too long\n");
664 SAFE_FREE(out);
665 return 1;
667 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
668 || (strncmp(data, "workg", 5) == 0)) {
669 /* note this allows for synonyms of "domain"
670 such as "DOM" and "dom" and "workgroup"
671 and "WORKGRP" etc. */
672 if (!value || !*value) {
673 fprintf(stderr, "CIFS: invalid domain name\n");
674 SAFE_FREE(out);
675 return 1; /* needs_arg; */
677 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
678 got_domain = 1;
679 } else {
680 fprintf(stderr, "domain name too long\n");
681 SAFE_FREE(out);
682 return 1;
684 } else if (strncmp(data, "cred", 4) == 0) {
685 if (value && *value) {
686 rc = open_cred_file(value);
687 if(rc) {
688 fprintf(stderr, "error %d (%s) opening credential file %s\n",
689 rc, strerror(rc), value);
690 SAFE_FREE(out);
691 return 1;
693 } else {
694 fprintf(stderr, "invalid credential file name specified\n");
695 SAFE_FREE(out);
696 return 1;
698 } else if (strncmp(data, "uid", 3) == 0) {
699 if (value && *value) {
700 got_uid = 1;
701 if (!isdigit(*value)) {
702 struct passwd *pw;
704 if (!(pw = getpwnam(value))) {
705 fprintf(stderr, "bad user name \"%s\"\n", value);
706 exit(EX_USAGE);
708 snprintf(user, sizeof(user), "%u", pw->pw_uid);
709 } else {
710 strlcpy(user,value,sizeof(user));
713 goto nocopy;
714 } else if (strncmp(data, "gid", 3) == 0) {
715 if (value && *value) {
716 got_gid = 1;
717 if (!isdigit(*value)) {
718 struct group *gr;
720 if (!(gr = getgrnam(value))) {
721 fprintf(stderr, "bad group name \"%s\"\n", value);
722 exit(EX_USAGE);
724 snprintf(group, sizeof(group), "%u", gr->gr_gid);
725 } else {
726 strlcpy(group,value,sizeof(group));
729 goto nocopy;
730 /* fmask and dmask synonyms for people used to smbfs syntax */
731 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
732 if (!value || !*value) {
733 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
734 SAFE_FREE(out);
735 return 1;
738 if (value[0] != '0') {
739 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
742 if (strcmp (data, "fmask") == 0) {
743 fprintf(stderr, "WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
744 data = "file_mode"; /* BB fix this */
746 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
747 if (!value || !*value) {
748 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
749 SAFE_FREE(out);
750 return 1;
753 if (value[0] != '0') {
754 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
757 if (strcmp (data, "dmask") == 0) {
758 fprintf(stderr, "WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
759 data = "dir_mode";
761 /* the following eight mount options should be
762 stripped out from what is passed into the kernel
763 since these eight options are best passed as the
764 mount flags rather than redundantly to the kernel
765 and could generate spurious warnings depending on the
766 level of the corresponding cifs vfs kernel code */
767 } else if (strncmp(data, "nosuid", 6) == 0) {
768 *filesys_flags |= MS_NOSUID;
769 } else if (strncmp(data, "suid", 4) == 0) {
770 *filesys_flags &= ~MS_NOSUID;
771 } else if (strncmp(data, "nodev", 5) == 0) {
772 *filesys_flags |= MS_NODEV;
773 } else if ((strncmp(data, "nobrl", 5) == 0) ||
774 (strncmp(data, "nolock", 6) == 0)) {
775 *filesys_flags &= ~MS_MANDLOCK;
776 } else if (strncmp(data, "dev", 3) == 0) {
777 *filesys_flags &= ~MS_NODEV;
778 } else if (strncmp(data, "noexec", 6) == 0) {
779 *filesys_flags |= MS_NOEXEC;
780 } else if (strncmp(data, "exec", 4) == 0) {
781 *filesys_flags &= ~MS_NOEXEC;
782 } else if (strncmp(data, "guest", 5) == 0) {
783 user_name = (char *)calloc(1, 1);
784 got_user = 1;
785 got_password = 1;
786 } else if (strncmp(data, "ro", 2) == 0) {
787 *filesys_flags |= MS_RDONLY;
788 goto nocopy;
789 } else if (strncmp(data, "rw", 2) == 0) {
790 *filesys_flags &= ~MS_RDONLY;
791 goto nocopy;
792 } else if (strncmp(data, "remount", 7) == 0) {
793 *filesys_flags |= MS_REMOUNT;
794 } /* else if (strnicmp(data, "port", 4) == 0) {
795 if (value && *value) {
796 vol->port =
797 simple_strtoul(value, &value, 0);
799 } else if (strnicmp(data, "rsize", 5) == 0) {
800 if (value && *value) {
801 vol->rsize =
802 simple_strtoul(value, &value, 0);
804 } else if (strnicmp(data, "wsize", 5) == 0) {
805 if (value && *value) {
806 vol->wsize =
807 simple_strtoul(value, &value, 0);
809 } else if (strnicmp(data, "version", 3) == 0) {
810 } else {
811 fprintf(stderr, "CIFS: Unknown mount option %s\n",data);
812 } */ /* nothing to do on those four mount options above.
813 Just pass to kernel and ignore them here */
815 /* Copy (possibly modified) option to out */
816 word_len = strlen(data);
817 if (value)
818 word_len += 1 + strlen(value);
820 out = (char *)realloc(out, out_len + word_len + 2);
821 if (out == NULL) {
822 perror("malloc");
823 exit(EX_SYSERR);
826 if (out_len) {
827 strlcat(out, ",", out_len + word_len + 2);
828 out_len++;
831 if (value)
832 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
833 else
834 snprintf(out + out_len, word_len + 1, "%s", data);
835 out_len = strlen(out);
837 nocopy:
838 data = next_keyword;
841 /* special-case the uid and gid */
842 if (got_uid) {
843 word_len = strlen(user);
845 out = (char *)realloc(out, out_len + word_len + 6);
846 if (out == NULL) {
847 perror("malloc");
848 exit(EX_SYSERR);
851 if (out_len) {
852 strlcat(out, ",", out_len + word_len + 6);
853 out_len++;
855 snprintf(out + out_len, word_len + 5, "uid=%s", user);
856 out_len = strlen(out);
858 if (got_gid) {
859 word_len = strlen(group);
861 out = (char *)realloc(out, out_len + 1 + word_len + 6);
862 if (out == NULL) {
863 perror("malloc");
864 exit(EX_SYSERR);
867 if (out_len) {
868 strlcat(out, ",", out_len + word_len + 6);
869 out_len++;
871 snprintf(out + out_len, word_len + 5, "gid=%s", group);
872 out_len = strlen(out);
875 SAFE_FREE(*optionsp);
876 *optionsp = out;
877 return 0;
880 /* replace all (one or more) commas with double commas */
881 static void check_for_comma(char ** ppasswrd)
883 char *new_pass_buf;
884 char *pass;
885 int i,j;
886 int number_of_commas = 0;
887 int len;
889 if(ppasswrd == NULL)
890 return;
891 else
892 (pass = *ppasswrd);
894 len = strlen(pass);
896 for(i=0;i<len;i++) {
897 if(pass[i] == ',')
898 number_of_commas++;
901 if(number_of_commas == 0)
902 return;
903 if(number_of_commas > MOUNT_PASSWD_SIZE) {
904 /* would otherwise overflow the mount options buffer */
905 fprintf(stderr, "\nInvalid password. Password contains too many commas.\n");
906 return;
909 new_pass_buf = (char *)malloc(len+number_of_commas+1);
910 if(new_pass_buf == NULL)
911 return;
913 for(i=0,j=0;i<len;i++,j++) {
914 new_pass_buf[j] = pass[i];
915 if(pass[i] == ',') {
916 j++;
917 new_pass_buf[j] = pass[i];
920 new_pass_buf[len+number_of_commas] = 0;
922 SAFE_FREE(*ppasswrd);
923 *ppasswrd = new_pass_buf;
925 return;
928 /* Usernames can not have backslash in them and we use
929 [BB check if usernames can have forward slash in them BB]
930 backslash as domain\user separator character
932 static char * check_for_domain(char **ppuser)
934 char * original_string;
935 char * usernm;
936 char * domainnm;
937 int original_len;
938 int len;
939 int i;
941 if(ppuser == NULL)
942 return NULL;
944 original_string = *ppuser;
946 if (original_string == NULL)
947 return NULL;
949 original_len = strlen(original_string);
951 usernm = strchr(*ppuser,'/');
952 if (usernm == NULL) {
953 usernm = strchr(*ppuser,'\\');
954 if (usernm == NULL)
955 return NULL;
958 if(got_domain) {
959 fprintf(stderr, "Domain name specified twice. Username probably malformed\n");
960 return NULL;
963 usernm[0] = 0;
964 domainnm = *ppuser;
965 if (domainnm[0] != 0) {
966 got_domain = 1;
967 } else {
968 fprintf(stderr, "null domain\n");
970 len = strlen(domainnm);
971 /* reset domainm to new buffer, and copy
972 domain name into it */
973 domainnm = (char *)malloc(len+1);
974 if(domainnm == NULL)
975 return NULL;
977 strlcpy(domainnm,*ppuser,len+1);
979 /* move_string(*ppuser, usernm+1) */
980 len = strlen(usernm+1);
982 if(len >= original_len) {
983 /* should not happen */
984 return domainnm;
987 for(i=0;i<original_len;i++) {
988 if(i<len)
989 original_string[i] = usernm[i+1];
990 else /* stuff with commas to remove last parm */
991 original_string[i] = ',';
994 /* BB add check for more than one slash?
995 strchr(*ppuser,'/');
996 strchr(*ppuser,'\\')
999 return domainnm;
1002 /* replace all occurances of "from" in a string with "to" */
1003 static void replace_char(char *string, char from, char to, int maxlen)
1005 char *lastchar = string + maxlen;
1006 while (string) {
1007 string = strchr(string, from);
1008 if (string) {
1009 *string = to;
1010 if (string >= lastchar)
1011 return;
1016 /* Note that caller frees the returned buffer if necessary */
1017 static struct addrinfo *
1018 parse_server(char ** punc_name)
1020 char * unc_name = *punc_name;
1021 int length = strnlen(unc_name, MAX_UNC_LEN);
1022 char * share;
1023 struct addrinfo *addrlist;
1024 int rc;
1026 if(length > (MAX_UNC_LEN - 1)) {
1027 fprintf(stderr, "mount error: UNC name too long");
1028 return NULL;
1030 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1031 (strncasecmp("smb://", unc_name, 6) == 0)) {
1032 fprintf(stderr, "\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
1033 return NULL;
1036 if(length < 3) {
1037 /* BB add code to find DFS root here */
1038 fprintf(stderr, "\nMounting the DFS root for domain not implemented yet\n");
1039 return NULL;
1040 } else {
1041 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
1042 /* check for nfs syntax ie server:share */
1043 share = strchr(unc_name,':');
1044 if(share) {
1045 *punc_name = (char *)malloc(length+3);
1046 if(*punc_name == NULL) {
1047 /* put the original string back if
1048 no memory left */
1049 *punc_name = unc_name;
1050 return NULL;
1052 *share = '/';
1053 strlcpy((*punc_name)+2,unc_name,length+1);
1054 SAFE_FREE(unc_name);
1055 unc_name = *punc_name;
1056 unc_name[length+2] = 0;
1057 goto continue_unc_parsing;
1058 } else {
1059 fprintf(stderr, "mount error: improperly formatted UNC name.");
1060 fprintf(stderr, " %s does not begin with \\\\ or //\n",unc_name);
1061 return NULL;
1063 } else {
1064 continue_unc_parsing:
1065 unc_name[0] = '/';
1066 unc_name[1] = '/';
1067 unc_name += 2;
1069 /* allow for either delimiter between host and sharename */
1070 if ((share = strpbrk(unc_name, "/\\"))) {
1071 *share = 0; /* temporarily terminate the string */
1072 share += 1;
1073 if(got_ip == 0) {
1074 rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
1075 if (rc != 0) {
1076 fprintf(stderr, "mount error: could not resolve address for %s: %s\n",
1077 unc_name, gai_strerror(rc));
1078 addrlist = NULL;
1081 *(share - 1) = '/'; /* put delimiter back */
1083 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1084 if ((prefixpath = strpbrk(share, "/\\"))) {
1085 *prefixpath = 0; /* permanently terminate the string */
1086 if (!strlen(++prefixpath))
1087 prefixpath = NULL; /* this needs to be done explicitly */
1089 if(got_ip) {
1090 if(verboseflag)
1091 fprintf(stderr, "ip address specified explicitly\n");
1092 return NULL;
1094 /* BB should we pass an alternate version of the share name as Unicode */
1096 return addrlist;
1097 } else {
1098 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1099 fprintf(stderr, "Mounting the DFS root for a particular server not implemented yet\n");
1100 return NULL;
1106 static struct option longopts[] = {
1107 { "all", 0, NULL, 'a' },
1108 { "help",0, NULL, 'h' },
1109 { "move",0, NULL, 'm' },
1110 { "bind",0, NULL, 'b' },
1111 { "read-only", 0, NULL, 'r' },
1112 { "ro", 0, NULL, 'r' },
1113 { "verbose", 0, NULL, 'v' },
1114 { "version", 0, NULL, 'V' },
1115 { "read-write", 0, NULL, 'w' },
1116 { "rw", 0, NULL, 'w' },
1117 { "options", 1, NULL, 'o' },
1118 { "type", 1, NULL, 't' },
1119 { "rsize",1, NULL, 'R' },
1120 { "wsize",1, NULL, 'W' },
1121 { "uid", 1, NULL, '1'},
1122 { "gid", 1, NULL, '2'},
1123 { "user",1,NULL,'u'},
1124 { "username",1,NULL,'u'},
1125 { "dom",1,NULL,'d'},
1126 { "domain",1,NULL,'d'},
1127 { "password",1,NULL,'p'},
1128 { "pass",1,NULL,'p'},
1129 { "credentials",1,NULL,'c'},
1130 { "port",1,NULL,'P'},
1131 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1132 { NULL, 0, NULL, 0 }
1135 /* convert a string to uppercase. return false if the string
1136 * wasn't ASCII. Return success on a NULL ptr */
1137 static int
1138 uppercase_string(char *string)
1140 if (!string)
1141 return 1;
1143 while (*string) {
1144 /* check for unicode */
1145 if ((unsigned char) string[0] & 0x80)
1146 return 0;
1147 *string = toupper((unsigned char) *string);
1148 string++;
1151 return 1;
1154 static void print_cifs_mount_version(void)
1156 printf("mount.cifs version: %s.%s%s\n",
1157 MOUNT_CIFS_VERSION_MAJOR,
1158 MOUNT_CIFS_VERSION_MINOR,
1159 MOUNT_CIFS_VENDOR_SUFFIX);
1162 int main(int argc, char ** argv)
1164 int c;
1165 unsigned long flags = MS_MANDLOCK;
1166 char * orgoptions = NULL;
1167 char * share_name = NULL;
1168 const char * ipaddr = NULL;
1169 char * uuid = NULL;
1170 char * mountpoint = NULL;
1171 char * options = NULL;
1172 char * optionstail;
1173 char * resolved_path = NULL;
1174 char * temp;
1175 char * dev_name;
1176 int rc = 0;
1177 int rsize = 0;
1178 int wsize = 0;
1179 int nomtab = 0;
1180 int uid = 0;
1181 int gid = 0;
1182 int optlen = 0;
1183 int orgoptlen = 0;
1184 size_t options_size = 0;
1185 size_t current_len;
1186 int retry = 0; /* set when we have to retry mount with uppercase */
1187 struct addrinfo *addrhead = NULL, *addr;
1188 struct utsname sysinfo;
1189 struct mntent mountent;
1190 struct sockaddr_in *addr4;
1191 struct sockaddr_in6 *addr6;
1192 FILE * pmntfile;
1194 /* setlocale(LC_ALL, "");
1195 bindtextdomain(PACKAGE, LOCALEDIR);
1196 textdomain(PACKAGE); */
1198 if(argc && argv)
1199 thisprogram = argv[0];
1200 else
1201 mount_cifs_usage(stderr);
1203 if(thisprogram == NULL)
1204 thisprogram = "mount.cifs";
1206 uname(&sysinfo);
1207 /* BB add workstation name and domain and pass down */
1209 /* #ifdef _GNU_SOURCE
1210 fprintf(stderr, " node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1211 #endif */
1212 if(argc > 2) {
1213 dev_name = argv[1];
1214 share_name = strndup(argv[1], MAX_UNC_LEN);
1215 if (share_name == NULL) {
1216 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1217 exit(EX_SYSERR);
1219 mountpoint = argv[2];
1220 } else if (argc == 2) {
1221 if ((strcmp(argv[1], "-V") == 0) ||
1222 (strcmp(argv[1], "--version") == 0))
1224 print_cifs_mount_version();
1225 exit(0);
1228 if ((strcmp(argv[1], "-h") == 0) ||
1229 (strcmp(argv[1], "-?") == 0) ||
1230 (strcmp(argv[1], "--help") == 0))
1231 mount_cifs_usage(stdout);
1233 mount_cifs_usage(stderr);
1234 } else {
1235 mount_cifs_usage(stderr);
1239 /* add sharename in opts string as unc= parm */
1240 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1241 longopts, NULL)) != -1) {
1242 switch (c) {
1243 /* No code to do the following options yet */
1244 /* case 'l':
1245 list_with_volumelabel = 1;
1246 break;
1247 case 'L':
1248 volumelabel = optarg;
1249 break; */
1250 /* case 'a':
1251 ++mount_all;
1252 break; */
1254 case '?':
1255 case 'h': /* help */
1256 mount_cifs_usage(stdout);
1257 case 'n':
1258 ++nomtab;
1259 break;
1260 case 'b':
1261 #ifdef MS_BIND
1262 flags |= MS_BIND;
1263 #else
1264 fprintf(stderr,
1265 "option 'b' (MS_BIND) not supported\n");
1266 #endif
1267 break;
1268 case 'm':
1269 #ifdef MS_MOVE
1270 flags |= MS_MOVE;
1271 #else
1272 fprintf(stderr,
1273 "option 'm' (MS_MOVE) not supported\n");
1274 #endif
1275 break;
1276 case 'o':
1277 orgoptions = strdup(optarg);
1278 break;
1279 case 'r': /* mount readonly */
1280 flags |= MS_RDONLY;
1281 break;
1282 case 'U':
1283 uuid = optarg;
1284 break;
1285 case 'v':
1286 ++verboseflag;
1287 break;
1288 case 'V':
1289 print_cifs_mount_version();
1290 exit (0);
1291 case 'w':
1292 flags &= ~MS_RDONLY;
1293 break;
1294 case 'R':
1295 rsize = atoi(optarg) ;
1296 break;
1297 case 'W':
1298 wsize = atoi(optarg);
1299 break;
1300 case '1':
1301 if (isdigit(*optarg)) {
1302 char *ep;
1304 uid = strtoul(optarg, &ep, 10);
1305 if (*ep) {
1306 fprintf(stderr, "bad uid value \"%s\"\n", optarg);
1307 exit(EX_USAGE);
1309 } else {
1310 struct passwd *pw;
1312 if (!(pw = getpwnam(optarg))) {
1313 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1314 exit(EX_USAGE);
1316 uid = pw->pw_uid;
1317 endpwent();
1319 break;
1320 case '2':
1321 if (isdigit(*optarg)) {
1322 char *ep;
1324 gid = strtoul(optarg, &ep, 10);
1325 if (*ep) {
1326 fprintf(stderr, "bad gid value \"%s\"\n", optarg);
1327 exit(EX_USAGE);
1329 } else {
1330 struct group *gr;
1332 if (!(gr = getgrnam(optarg))) {
1333 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1334 exit(EX_USAGE);
1336 gid = gr->gr_gid;
1337 endpwent();
1339 break;
1340 case 'u':
1341 got_user = 1;
1342 user_name = optarg;
1343 break;
1344 case 'd':
1345 domain_name = optarg; /* BB fix this - currently ignored */
1346 got_domain = 1;
1347 break;
1348 case 'p':
1349 if(mountpassword == NULL)
1350 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1351 if(mountpassword) {
1352 got_password = 1;
1353 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1355 break;
1356 case 'S':
1357 get_password_from_file(0 /* stdin */,NULL);
1358 break;
1359 case 't':
1360 break;
1361 case 'f':
1362 ++fakemnt;
1363 break;
1364 default:
1365 fprintf(stderr, "unknown mount option %c\n",c);
1366 mount_cifs_usage(stderr);
1370 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1371 mount_cifs_usage(stderr);
1374 /* make sure mountpoint is legit */
1375 rc = check_mountpoint(thisprogram, mountpoint);
1376 if (rc)
1377 goto mount_exit;
1379 /* sanity check for unprivileged mounts */
1380 if (getuid()) {
1381 rc = check_fstab(thisprogram, mountpoint, dev_name,
1382 &orgoptions);
1383 if (rc)
1384 goto mount_exit;
1386 /* enable any default user mount flags */
1387 flags |= CIFS_SETUID_FLAGS;
1390 if (getenv("PASSWD")) {
1391 if(mountpassword == NULL)
1392 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1393 if(mountpassword) {
1394 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1395 got_password = 1;
1397 } else if (getenv("PASSWD_FD")) {
1398 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1399 } else if (getenv("PASSWD_FILE")) {
1400 get_password_from_file(0, getenv("PASSWD_FILE"));
1403 if (orgoptions && parse_options(&orgoptions, &flags)) {
1404 rc = EX_USAGE;
1405 goto mount_exit;
1408 if (getuid()) {
1409 #if !CIFS_LEGACY_SETUID_CHECK
1410 if (!(flags & (MS_USERS|MS_USER))) {
1411 fprintf(stderr, "%s: permission denied\n", thisprogram);
1412 rc = EX_USAGE;
1413 goto mount_exit;
1415 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1417 if (geteuid()) {
1418 fprintf(stderr, "%s: not installed setuid - \"user\" "
1419 "CIFS mounts not supported.",
1420 thisprogram);
1421 rc = EX_FAIL;
1422 goto mount_exit;
1426 flags &= ~(MS_USERS|MS_USER);
1428 addrhead = addr = parse_server(&share_name);
1429 if((addrhead == NULL) && (got_ip == 0)) {
1430 fprintf(stderr, "No ip address specified and hostname not found\n");
1431 rc = EX_USAGE;
1432 goto mount_exit;
1435 /* BB save off path and pop after mount returns? */
1436 resolved_path = (char *)malloc(PATH_MAX+1);
1437 if(resolved_path) {
1438 /* Note that if we can not canonicalize the name, we get
1439 another chance to see if it is valid when we chdir to it */
1440 if (realpath(mountpoint, resolved_path)) {
1441 mountpoint = resolved_path;
1444 if(got_user == 0) {
1445 /* Note that the password will not be retrieved from the
1446 USER env variable (ie user%password form) as there is
1447 already a PASSWD environment varaible */
1448 if (getenv("USER"))
1449 user_name = strdup(getenv("USER"));
1450 if (user_name == NULL)
1451 user_name = getusername();
1452 got_user = 1;
1455 if(got_password == 0) {
1456 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1457 no good replacement yet. */
1458 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1459 if (!tmp_pass || !mountpassword) {
1460 fprintf(stderr, "Password not entered, exiting\n");
1461 exit(EX_USAGE);
1463 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1464 got_password = 1;
1466 /* FIXME launch daemon (handles dfs name resolution and credential change)
1467 remember to clear parms and overwrite password field before launching */
1468 if(orgoptions) {
1469 optlen = strlen(orgoptions);
1470 orgoptlen = optlen;
1471 } else
1472 optlen = 0;
1473 if(share_name)
1474 optlen += strlen(share_name) + 4;
1475 else {
1476 fprintf(stderr, "No server share name specified\n");
1477 fprintf(stderr, "\nMounting the DFS root for server not implemented yet\n");
1478 exit(EX_USAGE);
1480 if(user_name)
1481 optlen += strlen(user_name) + 6;
1482 optlen += MAX_ADDRESS_LEN + 4;
1483 if(mountpassword)
1484 optlen += strlen(mountpassword) + 6;
1485 mount_retry:
1486 SAFE_FREE(options);
1487 options_size = optlen + 10 + DOMAIN_SIZE;
1488 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 */);
1490 if(options == NULL) {
1491 fprintf(stderr, "Could not allocate memory for mount options\n");
1492 exit(EX_SYSERR);
1495 strlcpy(options, "unc=", options_size);
1496 strlcat(options,share_name,options_size);
1497 /* scan backwards and reverse direction of slash */
1498 temp = strrchr(options, '/');
1499 if(temp > options + 6)
1500 *temp = '\\';
1501 if(user_name) {
1502 /* check for syntax like user=domain\user */
1503 if(got_domain == 0)
1504 domain_name = check_for_domain(&user_name);
1505 strlcat(options,",user=",options_size);
1506 strlcat(options,user_name,options_size);
1508 if(retry == 0) {
1509 if(domain_name) {
1510 /* extra length accounted for in option string above */
1511 strlcat(options,",domain=",options_size);
1512 strlcat(options,domain_name,options_size);
1515 if(mountpassword) {
1516 /* Commas have to be doubled, or else they will
1517 look like the parameter separator */
1518 /* if(sep is not set)*/
1519 if(retry == 0)
1520 check_for_comma(&mountpassword);
1521 strlcat(options,",pass=",options_size);
1522 strlcat(options,mountpassword,options_size);
1525 strlcat(options,",ver=",options_size);
1526 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1528 if(orgoptions) {
1529 strlcat(options,",",options_size);
1530 strlcat(options,orgoptions,options_size);
1532 if(prefixpath) {
1533 strlcat(options,",prefixpath=",options_size);
1534 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1536 if(verboseflag)
1537 fprintf(stderr, "\nmount.cifs kernel mount options %s \n",options);
1539 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1540 replace_char(dev_name, '\\', '/', strlen(share_name));
1542 if (!got_ip && addr) {
1543 strlcat(options, ",ip=", options_size);
1544 current_len = strnlen(options, options_size);
1545 optionstail = options + current_len;
1546 switch (addr->ai_addr->sa_family) {
1547 case AF_INET6:
1548 addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1549 ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1550 options_size - current_len);
1551 break;
1552 case AF_INET:
1553 addr4 = (struct sockaddr_in *) addr->ai_addr;
1554 ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1555 options_size - current_len);
1556 break;
1557 default:
1558 ipaddr = NULL;
1561 /* if the address looks bogus, try the next one */
1562 if (!ipaddr) {
1563 addr = addr->ai_next;
1564 if (addr)
1565 goto mount_retry;
1566 rc = EX_SYSERR;
1567 goto mount_exit;
1571 if (addr->ai_addr->sa_family == AF_INET6 && addr6->sin6_scope_id) {
1572 strlcat(options, "%", options_size);
1573 current_len = strnlen(options, options_size);
1574 optionstail = options + current_len;
1575 snprintf(optionstail, options_size - current_len, "%u",
1576 addr6->sin6_scope_id);
1579 if (!fakemnt && mount(dev_name, mountpoint, "cifs", flags, options)) {
1580 switch (errno) {
1581 case ECONNREFUSED:
1582 case EHOSTUNREACH:
1583 if (addr) {
1584 addr = addr->ai_next;
1585 if (addr)
1586 goto mount_retry;
1588 break;
1589 case ENODEV:
1590 fprintf(stderr, "mount error: cifs filesystem not supported by the system\n");
1591 break;
1592 case ENXIO:
1593 if(retry == 0) {
1594 retry = 1;
1595 if (uppercase_string(dev_name) &&
1596 uppercase_string(share_name) &&
1597 uppercase_string(prefixpath)) {
1598 fprintf(stderr, "retrying with upper case share name\n");
1599 goto mount_retry;
1603 fprintf(stderr, "mount error(%d): %s\n", errno, strerror(errno));
1604 fprintf(stderr, "Refer to the mount.cifs(8) manual page (e.g. man "
1605 "mount.cifs)\n");
1606 rc = EX_FAIL;
1607 goto mount_exit;
1610 if (nomtab)
1611 goto mount_exit;
1612 atexit(unlock_mtab);
1613 rc = lock_mtab();
1614 if (rc) {
1615 fprintf(stderr, "cannot lock mtab");
1616 goto mount_exit;
1618 pmntfile = setmntent(MOUNTED, "a+");
1619 if (!pmntfile) {
1620 fprintf(stderr, "could not update mount table\n");
1621 unlock_mtab();
1622 rc = EX_FILEIO;
1623 goto mount_exit;
1625 mountent.mnt_fsname = dev_name;
1626 mountent.mnt_dir = mountpoint;
1627 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1628 mountent.mnt_opts = (char *)malloc(220);
1629 if(mountent.mnt_opts) {
1630 char * mount_user = getusername();
1631 memset(mountent.mnt_opts,0,200);
1632 if(flags & MS_RDONLY)
1633 strlcat(mountent.mnt_opts,"ro",220);
1634 else
1635 strlcat(mountent.mnt_opts,"rw",220);
1636 if(flags & MS_MANDLOCK)
1637 strlcat(mountent.mnt_opts,",mand",220);
1638 if(flags & MS_NOEXEC)
1639 strlcat(mountent.mnt_opts,",noexec",220);
1640 if(flags & MS_NOSUID)
1641 strlcat(mountent.mnt_opts,",nosuid",220);
1642 if(flags & MS_NODEV)
1643 strlcat(mountent.mnt_opts,",nodev",220);
1644 if(flags & MS_SYNCHRONOUS)
1645 strlcat(mountent.mnt_opts,",sync",220);
1646 if(mount_user) {
1647 if(getuid() != 0) {
1648 strlcat(mountent.mnt_opts,
1649 ",user=", 220);
1650 strlcat(mountent.mnt_opts,
1651 mount_user, 220);
1655 mountent.mnt_freq = 0;
1656 mountent.mnt_passno = 0;
1657 rc = addmntent(pmntfile,&mountent);
1658 endmntent(pmntfile);
1659 unlock_mtab();
1660 SAFE_FREE(mountent.mnt_opts);
1661 if (rc)
1662 rc = EX_FILEIO;
1663 mount_exit:
1664 if(mountpassword) {
1665 int len = strlen(mountpassword);
1666 memset(mountpassword,0,len);
1667 SAFE_FREE(mountpassword);
1670 if (addrhead)
1671 freeaddrinfo(addrhead);
1672 SAFE_FREE(options);
1673 SAFE_FREE(orgoptions);
1674 SAFE_FREE(resolved_path);
1675 SAFE_FREE(share_name);
1676 exit(rc);