mount.cifs: on 2nd try mount.cifs must also uppercase "orig_dev"
[cifs-utils.git] / mount.cifs.c
1 /*
2  * Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3  * Copyright (C) 2003,2010 Steve French  (sfrench@us.ibm.com)
4  * Copyright (C) 2008 Jeremy Allison (jra@samba.org)
5  * Copyright (C) 2010 Jeff Layton (jlayton@samba.org)
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif /* HAVE_CONFIG_H */
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <pwd.h>
29 #include <grp.h>
30 #include <ctype.h>
31 #include <sys/types.h>
32 #include <sys/mount.h>
33 #include <sys/stat.h>
34 #include <sys/utsname.h>
35 #include <sys/socket.h>
36 #include <arpa/inet.h>
37 #include <getopt.h>
38 #include <errno.h>
39 #include <netdb.h>
40 #include <string.h>
41 #include <mntent.h>
42 #include <fcntl.h>
43 #include <limits.h>
44 #include <paths.h>
45 #include <libgen.h>
46 #include <sys/mman.h>
47 #include <sys/wait.h>
48 #include <stdbool.h>
49 #ifdef HAVE_SYS_FSUID_H
50 #include <sys/fsuid.h>
51 #endif /* HAVE_SYS_FSUID_H */
52 #ifdef HAVE_LIBCAP_NG
53 #include <cap-ng.h>
54 #else /* HAVE_LIBCAP_NG */
55 #ifdef HAVE_PRCTL
56 #include <sys/prctl.h>
57 #endif /* HAVE_PRCTL */
58 #ifdef HAVE_LIBCAP
59 #include <sys/capability.h>
60 #endif /* HAVE_LIBCAP */
61 #endif /* HAVE_LIBCAP_NG */
62 #include "mount.h"
63 #include "util.h"
64 #include "resolve_host.h"
65
66 #ifndef MS_MOVE 
67 #define MS_MOVE 8192 
68 #endif 
69
70 #ifndef MS_BIND
71 #define MS_BIND 4096
72 #endif
73
74 /* private flags - clear these before passing to kernel */
75 #define MS_USERS        0x40000000
76 #define MS_USER         0x80000000
77
78 #define MAX_UNC_LEN 1024
79
80 /* I believe that the kernel limits options data to a page */
81 #define MAX_OPTIONS_LEN 4096
82
83 /* max length of mtab options */
84 #define MTAB_OPTIONS_LEN 220
85
86 /*
87  * Max share name, username, password and domain sizes match the kernel's
88  * allowances for these string sizes which in turn match Microsoft's
89  * documentation.
90  */
91
92 /* Max length of the share name portion of a UNC. Share names over 80
93  * characters cannot be accessed via commandline in Windows 2000/XP. */
94 #define MAX_SHARE_LEN 256
95
96 /* Max user name length. */
97 #define MAX_USERNAME_SIZE 256
98
99 /* Max domain size. */
100 #define MAX_DOMAIN_SIZE 256
101
102 /* Max password size. */
103 #define MOUNT_PASSWD_SIZE 512
104
105
106
107 #ifndef SAFE_FREE
108 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x = NULL; } } while (0)
109 #endif
110
111
112 /*
113  * mount.cifs has been the subject of many "security" bugs that have arisen
114  * because of users and distributions installing it as a setuid root program
115  * before it had been audited for security holes. The default behavior is
116  * now to allow mount.cifs to be run as a setuid root program. Some admins
117  * may want to disable this fully, so this switch remains in place.
118  */
119 #define CIFS_DISABLE_SETUID_CAPABILITY 0
120
121 /*
122  * When an unprivileged user runs a setuid mount.cifs, we set certain mount
123  * flags by default. These defaults can be changed here.
124  */
125 #define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
126
127 /*
128  * Values for parsing a credentials file.
129  */
130 #define CRED_UNPARSEABLE 0
131 #define CRED_USER        1
132 #define CRED_PASS        2
133 #define CRED_DOM         4
134
135 /*
136  * Values for parsing command line options.
137  */
138 #define OPT_ERROR       -1
139 #define OPT_IGNORE      0
140 #define OPT_USERS       1
141 #define OPT_USER        2
142 #define OPT_USER_XATTR  3
143 #define OPT_PASS        4
144 #define OPT_SEC         5
145 #define OPT_IP          6
146 #define OPT_UNC         7
147 #define OPT_CRED        8
148 #define OPT_UID         9
149 #define OPT_GID        10
150 #define OPT_FMASK      11
151 #define OPT_FILE_MODE  12
152 #define OPT_DMASK      13
153 #define OPT_DIR_MODE   14
154 #define OPT_DOM        15
155 #define OPT_NO_SUID    16
156 #define OPT_SUID       17
157 #define OPT_NO_DEV     18
158 #define OPT_DEV        19
159 #define OPT_NO_LOCK    20
160 #define OPT_NO_EXEC    21
161 #define OPT_EXEC       22
162 #define OPT_GUEST      23
163 #define OPT_RO         24
164 #define OPT_RW         25
165 #define OPT_REMOUNT    26
166 #define OPT_MAND       27
167 #define OPT_NOMAND     28
168 #define OPT_CRUID      29
169 #define OPT_BKUPUID    30
170 #define OPT_BKUPGID    31
171 #define OPT_NOFAIL     32
172
173 #define MNT_TMP_FILE "/.mtab.cifs.XXXXXX"
174
175 /* struct for holding parsed mount info for use by privleged process */
176 struct parsed_mount_info {
177         unsigned long flags;
178         char host[NI_MAXHOST + 1];
179         char share[MAX_SHARE_LEN + 1];
180         char prefix[PATH_MAX + 1];
181         char options[MAX_OPTIONS_LEN];
182         char domain[MAX_DOMAIN_SIZE + 1];
183         char username[MAX_USERNAME_SIZE + 1];
184         char password[MOUNT_PASSWD_SIZE + 1];
185         char addrlist[MAX_ADDR_LIST_LEN];
186         unsigned int got_user:1;
187         unsigned int got_password:1;
188         unsigned int fakemnt:1;
189         unsigned int nomtab:1;
190         unsigned int verboseflag:1;
191         unsigned int nofail:1;
192 };
193
194 static const char *thisprogram;
195 static const char *cifs_fstype = "cifs";
196
197 static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info);
198
199 static int check_setuid(void)
200 {
201         if (geteuid()) {
202                 fprintf(stderr, "This program is not installed setuid root - "
203                         " \"user\" CIFS mounts not supported.\n");
204                 return EX_USAGE;
205         }
206
207 #if CIFS_DISABLE_SETUID_CAPABILITY
208         if (getuid() && !geteuid()) {
209                 printf("This mount.cifs program has been built with the "
210                        "ability to run as a setuid root program disabled.\n");
211                 return EX_USAGE;
212         }
213 #endif /* CIFS_DISABLE_SETUID_CAPABILITY */
214
215         return 0;
216 }
217
218 static int
219 check_fstab(const char *progname, const char *mountpoint, const char *devname,
220             char **options)
221 {
222         FILE *fstab;
223         struct mntent *mnt;
224
225         /* make sure this mount is listed in /etc/fstab */
226         fstab = setmntent(_PATH_MNTTAB, "r");
227         if (!fstab) {
228                 fprintf(stderr, "Couldn't open %s for reading!\n", _PATH_MNTTAB);
229                 return EX_FILEIO;
230         }
231
232         while ((mnt = getmntent(fstab))) {
233                 if (!strcmp(mountpoint, mnt->mnt_dir))
234                         break;
235         }
236         endmntent(fstab);
237
238         if (mnt == NULL || strcmp(mnt->mnt_fsname, devname)) {
239                 fprintf(stderr, "%s: permission denied: no match for "
240                         "%s found in %s\n", progname, mountpoint, _PATH_MNTTAB);
241                 return EX_USAGE;
242         }
243
244         /*
245          * 'mount' munges the options from fstab before passing them
246          * to us. It is non-trivial to test that we have the correct
247          * set of options. We don't want to trust what the user
248          * gave us, so just take whatever is in /etc/fstab.
249          */
250         free(*options);
251         *options = strdup(mnt->mnt_opts);
252         return 0;
253 }
254
255 /* BB finish BB
256
257         cifs_umount
258         open nofollow - avoid symlink exposure? 
259         get owner of dir see if matches self or if root
260         call system(umount argv) etc.
261
262 BB end finish BB */
263
264 static int mount_usage(FILE * stream)
265 {
266         fprintf(stream, "\nUsage:  %s <remotetarget> <dir> -o <options>\n",
267                 thisprogram);
268         fprintf(stream, "\nMount the remote target, specified as a UNC name,");
269         fprintf(stream, " to a local directory.\n\nOptions:\n");
270         fprintf(stream, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
271         fprintf(stream, "\nLess commonly used options:");
272         fprintf(stream,
273                 "\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
274         fprintf(stream,
275                 "\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
276         fprintf(stream,
277                 "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
278         fprintf(stream,
279                 "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign,fsc");
280         fprintf(stream,
281                 "\n\nOptions not needed for servers supporting CIFS Unix extensions");
282         fprintf(stream,
283                 "\n\t(e.g. unneeded for mounts to most Samba versions):");
284         fprintf(stream,
285                 "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
286         fprintf(stream, "\n\nRarely used options:");
287         fprintf(stream,
288                 "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
289         fprintf(stream,
290                 "\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
291         fprintf(stream,
292                 "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
293         fprintf(stream,
294                 "\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);
298
299         if (stream == stderr)
300                 return EX_USAGE;
301         return 0;
302 }
303
304 /*
305  * CIFS has to "escape" commas in the password field so that they don't
306  * end up getting confused for option delimiters. Copy password into pw
307  * field, turning any commas into double commas.
308  */
309 static int set_password(struct parsed_mount_info *parsed_info, const char *src)
310 {
311         char *dst = parsed_info->password;
312         unsigned int i = 0, j = 0;
313
314         while (src[i]) {
315                 if (src[i] == ',')
316                         dst[j++] = ',';
317                 dst[j++] = src[i++];
318                 if (j > sizeof(parsed_info->password)) {
319                         fprintf(stderr, "Converted password too long!\n");
320                         return EX_USAGE;
321                 }
322         }
323         dst[j] = '\0';
324         parsed_info->got_password = 1;
325         return 0;
326 }
327
328 #ifdef HAVE_LIBCAP_NG
329 static int
330 drop_capabilities(int parent)
331 {
332         capng_setpid(getpid());
333         capng_clear(CAPNG_SELECT_BOTH);
334         if (parent) {
335                 if (capng_updatev(CAPNG_ADD, CAPNG_PERMITTED, CAP_DAC_READ_SEARCH, CAP_DAC_OVERRIDE, -1)) {
336                         fprintf(stderr, "Unable to update capability set.\n");
337                         return EX_SYSERR;
338                 }
339                 if (capng_update(CAPNG_ADD, CAPNG_PERMITTED|CAPNG_EFFECTIVE, CAP_SYS_ADMIN)) {
340                         fprintf(stderr, "Unable to update capability set.\n");
341                         return EX_SYSERR;
342                 }
343         } else {
344                 if (capng_update(CAPNG_ADD, CAPNG_PERMITTED, CAP_DAC_READ_SEARCH)) {
345                         fprintf(stderr, "Unable to update capability set.\n");
346                         return EX_SYSERR;
347                 }
348         }
349         if (capng_apply(CAPNG_SELECT_BOTH)) {
350                 fprintf(stderr, "Unable to apply new capability set.\n");
351                 return EX_SYSERR;
352         }
353         return 0;
354 }
355
356 static int
357 toggle_dac_capability(int writable, int enable)
358 {
359         unsigned int capability = writable ? CAP_DAC_OVERRIDE : CAP_DAC_READ_SEARCH;
360
361         if (capng_update(enable ? CAPNG_ADD : CAPNG_DROP, CAPNG_EFFECTIVE, capability)) {
362                 fprintf(stderr, "Unable to update capability set.\n");
363                 return EX_SYSERR;
364         }
365         if (capng_apply(CAPNG_SELECT_CAPS)) {
366                 fprintf(stderr, "Unable to apply new capability set.\n");
367                 return EX_SYSERR;
368         }
369         return 0;
370 }
371 #else /* HAVE_LIBCAP_NG */
372 #ifdef HAVE_LIBCAP
373 #ifdef HAVE_PRCTL
374 static int
375 prune_bounding_set(void)
376 {
377         int i, rc = 0;
378         static int bounding_set_cleared;
379
380         if (bounding_set_cleared)
381                 return 0;
382
383         for (i = 0; i <= CAP_LAST_CAP && rc == 0; ++i)
384                 rc = prctl(PR_CAPBSET_DROP, i);
385
386         if (rc != 0) {
387                 fprintf(stderr, "Unable to clear capability bounding set: %d\n", rc);
388                 return EX_SYSERR;
389         }
390
391         ++bounding_set_cleared;
392         return 0;
393 }
394 #else /* HAVE_PRCTL */
395 static int
396 prune_bounding_set(void)
397 {
398         return 0;
399 }
400 #endif /* HAVE_PRCTL */
401 static int
402 drop_capabilities(int parent)
403 {
404         int rc, ncaps;
405         cap_t caps;
406         cap_value_t cap_list[3];
407
408         rc = prune_bounding_set();
409         if (rc)
410                 return rc;
411
412         caps = cap_get_proc();
413         if (caps == NULL) {
414                 fprintf(stderr, "Unable to get current capability set: %s\n",
415                         strerror(errno));
416                 return EX_SYSERR;
417         }
418
419         if (cap_clear(caps) == -1) {
420                 fprintf(stderr, "Unable to clear capability set: %s\n",
421                         strerror(errno));
422                 rc = EX_SYSERR;
423                 goto free_caps;
424         }
425
426         if (parent || getuid() == 0) {
427                 ncaps = 1;
428                 cap_list[0] = CAP_DAC_READ_SEARCH;
429                 if (parent) {
430                         cap_list[1] = CAP_DAC_OVERRIDE;
431                         cap_list[2] = CAP_SYS_ADMIN;
432                         ncaps += 2;
433                 }
434                 if (cap_set_flag(caps, CAP_PERMITTED, ncaps, cap_list, CAP_SET) == -1) {
435                         fprintf(stderr, "Unable to set permitted capabilities: %s\n",
436                                 strerror(errno));
437                         rc = EX_SYSERR;
438                         goto free_caps;
439                 }
440                 if (parent) {
441                         cap_list[0] = CAP_SYS_ADMIN;
442                         if (cap_set_flag(caps, CAP_EFFECTIVE, 1, cap_list, CAP_SET) == -1) {
443                                 fprintf(stderr, "Unable to set effective capabilities: %s\n",
444                                         strerror(errno));
445                                 rc = EX_SYSERR;
446                                 goto free_caps;
447                         }
448                 }
449         }
450
451         if (cap_set_proc(caps) != 0) {
452                 fprintf(stderr, "Unable to set current process capabilities: %s\n",
453                         strerror(errno));
454                 rc = EX_SYSERR;
455         }
456 free_caps:
457         cap_free(caps);
458         return rc;
459 }
460
461 static int
462 toggle_dac_capability(int writable, int enable)
463 {
464         int rc = 0;
465         cap_t caps;
466         cap_value_t capability = writable ? CAP_DAC_OVERRIDE : CAP_DAC_READ_SEARCH;
467
468         caps = cap_get_proc();
469         if (caps == NULL) {
470                 fprintf(stderr, "Unable to get current capability set: %s\n",
471                         strerror(errno));
472                 return EX_SYSERR;
473         }
474
475         if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &capability,
476                          enable ? CAP_SET : CAP_CLEAR) == -1) {
477                 fprintf(stderr, "Unable to %s effective capabilities: %s\n",
478                         enable ? "set" : "clear", strerror(errno));
479                 rc = EX_SYSERR;
480                 goto free_caps;
481         }
482
483         if (cap_set_proc(caps) != 0) {
484                 fprintf(stderr, "Unable to set current process capabilities: %s\n",
485                         strerror(errno));
486                 rc = EX_SYSERR;
487         }
488 free_caps:
489         cap_free(caps);
490         return rc;
491 }
492 #else /* HAVE_LIBCAP */
493 static int
494 drop_capabilities(int parent __attribute((unused)))
495 {
496         return 0;
497 }
498
499 static int
500 toggle_dac_capability(int writable __attribute((unused)), int enable __attribute((unused)))
501 {
502         return 0;
503 }
504 #endif /* HAVE_LIBCAP */
505 #endif /* HAVE_LIBCAP_NG */
506
507 static void null_terminate_endl(char *source)
508 {
509         char *newline = strchr(source, '\n');
510         if (newline)
511                 *newline = '\0';
512 }
513
514 /*
515  * Parse a line from the credentials file.  Changes target to first
516  * character after '=' on 'line' and returns the value type of the line
517  * Returns CRED_UNPARSEABLE on failure or if either parameter is NULL.
518  */
519 static int parse_cred_line(char *line, char **target)
520 {
521         if (line == NULL || target == NULL)
522                 goto parsing_err;
523
524         /* position target at first char of value */
525         *target = strchr(line, '=');
526         if (!*target)
527                 goto parsing_err;
528         *target += 1;
529
530         /* tell the caller which value target points to */
531         if (strncasecmp("user", line, 4) == 0)
532                 return CRED_USER;
533         if (strncasecmp("pass", line, 4) == 0)
534                 return CRED_PASS;
535         if (strncasecmp("dom", line, 3) == 0)
536                 return CRED_DOM;
537
538 parsing_err:
539         return CRED_UNPARSEABLE;
540 }
541
542 static int open_cred_file(char *file_name,
543                         struct parsed_mount_info *parsed_info)
544 {
545         char *line_buf = NULL;
546         char *temp_val = NULL;
547         FILE *fs = NULL;
548         int i;
549         const int line_buf_size = 4096;
550         const int min_non_white = 10;
551
552         i = toggle_dac_capability(0, 1);
553         if (i)
554                 goto return_i;
555
556         i = access(file_name, R_OK);
557         if (i) {
558                 toggle_dac_capability(0, 0);
559                 i = errno;
560                 goto return_i;
561         }
562
563         fs = fopen(file_name, "r");
564         if (fs == NULL) {
565                 toggle_dac_capability(0, 0);
566                 i = errno;
567                 goto return_i;
568         }
569
570         i = toggle_dac_capability(0, 0);
571         if (i)
572                 goto return_i;
573
574         line_buf = (char *)malloc(line_buf_size);
575         if (line_buf == NULL) {
576                 i = EX_SYSERR;
577                 goto return_i;
578         }
579
580         /* parse line from credentials file */
581         while (fgets(line_buf, line_buf_size, fs)) {
582                 /* eat leading white space */
583                 for (i = 0; i < line_buf_size - min_non_white + 1; i++) {
584                         if ((line_buf[i] != ' ') && (line_buf[i] != '\t'))
585                                 break;
586                 }
587                 null_terminate_endl(line_buf);
588
589                 /* parse next token */
590                 switch (parse_cred_line(line_buf + i, &temp_val)) {
591                 case CRED_USER:
592                         strlcpy(parsed_info->username, temp_val,
593                                 sizeof(parsed_info->username));
594                         parsed_info->got_user = 1;
595                         break;
596                 case CRED_PASS:
597                         i = set_password(parsed_info, temp_val);
598                         if (i)
599                                 goto return_i;
600                         break;
601                 case CRED_DOM:
602                         if (parsed_info->verboseflag)
603                                 fprintf(stderr, "domain=%s\n",
604                                         temp_val);
605                         strlcpy(parsed_info->domain, temp_val,
606                                 sizeof(parsed_info->domain));
607                         break;
608                 case CRED_UNPARSEABLE:
609                         if (parsed_info->verboseflag)
610                                 fprintf(stderr, "Credential formatted "
611                                         "incorrectly: %s\n",
612                                         temp_val ? temp_val : "(null)");
613                         break;
614                 }
615         }
616         i = 0;
617 return_i:
618         if (fs != NULL)
619                 fclose(fs);
620
621         /* make sure passwords are scrubbed from memory */
622         if (line_buf != NULL)
623                 memset(line_buf, 0, line_buf_size);
624         SAFE_FREE(line_buf);
625         return i;
626 }
627
628 static int
629 get_password_from_file(int file_descript, char *filename,
630                        struct parsed_mount_info *parsed_info)
631 {
632         int rc = 0;
633         char buf[sizeof(parsed_info->password) + 1];
634
635         if (filename != NULL) {
636                 rc = toggle_dac_capability(0, 1);
637                 if (rc)
638                         return rc;
639
640                 rc = access(filename, R_OK);
641                 if (rc) {
642                         fprintf(stderr,
643                                 "mount.cifs failed: access check of %s failed: %s\n",
644                                 filename, strerror(errno));
645                         toggle_dac_capability(0, 0);
646                         return EX_SYSERR;
647                 }
648
649                 file_descript = open(filename, O_RDONLY);
650                 if (file_descript < 0) {
651                         fprintf(stderr,
652                                 "mount.cifs failed. %s attempting to open password file %s\n",
653                                 strerror(errno), filename);
654                         toggle_dac_capability(0, 0);
655                         return EX_SYSERR;
656                 }
657
658                 rc = toggle_dac_capability(0, 0);
659                 if (rc) {
660                         rc = EX_SYSERR;
661                         goto get_pw_exit;
662                 }
663         }
664
665         memset(buf, 0, sizeof(buf));
666         rc = read(file_descript, buf, sizeof(buf) - 1);
667         if (rc < 0) {
668                 fprintf(stderr,
669                         "mount.cifs failed. Error %s reading password file\n",
670                         strerror(errno));
671                 rc = EX_SYSERR;
672                 goto get_pw_exit;
673         }
674
675         rc = set_password(parsed_info, buf);
676
677 get_pw_exit:
678         if (filename != NULL)
679                 close(file_descript);
680         return rc;
681 }
682
683 /*
684  * Returns OPT_ERROR on unparsable token.
685  */
686 static int parse_opt_token(const char *token)
687 {
688         if (token == NULL)
689                 return OPT_ERROR;
690
691         if (strncmp(token, "users", 5) == 0)
692                 return OPT_USERS;
693         if (strncmp(token, "user_xattr", 10) == 0)
694                 return OPT_USER_XATTR;
695         if (strncmp(token, "user", 4) == 0)
696                 return OPT_USER;
697         if (strncmp(token, "pass", 4) == 0)
698                 return OPT_PASS;
699         if (strncmp(token, "sec", 3) == 0)
700                 return OPT_SEC;
701         if (strncmp(token, "ip", 2) == 0)
702                 return OPT_IP;
703         if (strncmp(token, "unc", 3) == 0 ||
704                 strncmp(token, "target", 6) == 0 ||
705                 strncmp(token, "path", 4) == 0)
706                 return OPT_UNC;
707         if (strncmp(token, "dom", 3) == 0 || strncmp(token, "workg", 5) == 0)
708                 return OPT_DOM;
709         if (strncmp(token, "cred", 4) == 0)
710                 return OPT_CRED;
711         if (strncmp(token, "uid", 3) == 0)
712                 return OPT_UID;
713         if (strncmp(token, "cruid", 5) == 0)
714                 return OPT_CRUID;
715         if (strncmp(token, "gid", 3) == 0)
716                 return OPT_GID;
717         if (strncmp(token, "fmask", 5) == 0)
718                 return OPT_FMASK;
719         if (strncmp(token, "file_mode", 9) == 0)
720                 return OPT_FILE_MODE;
721         if (strncmp(token, "dmask", 5) == 0)
722                 return OPT_DMASK;
723         if (strncmp(token, "dir_mode", 4) == 0 || strncmp(token, "dirm", 4) == 0)
724                 return OPT_DIR_MODE;
725         if (strncmp(token, "nosuid", 6) == 0)
726                 return OPT_NO_SUID;
727         if (strncmp(token, "suid", 4) == 0)
728                 return OPT_SUID;
729         if (strncmp(token, "nodev", 5) == 0)
730                 return OPT_NO_DEV;
731         if (strncmp(token, "nobrl", 5) == 0 || strncmp(token, "nolock", 6) == 0)
732                 return OPT_NO_LOCK;
733         if (strncmp(token, "mand", 4) == 0)
734                 return OPT_MAND;
735         if (strncmp(token, "nomand", 6) == 0)
736                 return OPT_NOMAND;
737         if (strncmp(token, "dev", 3) == 0)
738                 return OPT_DEV;
739         if (strncmp(token, "noexec", 6) == 0)
740                 return OPT_NO_EXEC;
741         if (strncmp(token, "exec", 4) == 0)
742                 return OPT_EXEC;
743         if (strncmp(token, "guest", 5) == 0)
744                 return OPT_GUEST;
745         if (strncmp(token, "ro", 2) == 0)
746                 return OPT_RO;
747         if (strncmp(token, "rw", 2) == 0 && strlen(token) == 2)
748                 return OPT_RW;
749         if (strncmp(token, "remount", 7) == 0)
750                 return OPT_REMOUNT;
751         if (strncmp(token, "_netdev", 7) == 0)
752                 return OPT_IGNORE;
753         if (strncmp(token, "backupuid", 9) == 0)
754                 return OPT_BKUPUID;
755         if (strncmp(token, "backupgid", 9) == 0)
756                 return OPT_BKUPGID;
757         if (strncmp(token, "nofail", 6) == 0)
758                 return OPT_NOFAIL;
759
760         return OPT_ERROR;
761 }
762
763 static int
764 parse_options(const char *data, struct parsed_mount_info *parsed_info)
765 {
766         char *value = NULL;
767         char *equals = NULL;
768         char *next_keyword = NULL;
769         char *out = parsed_info->options;
770         unsigned long *filesys_flags = &parsed_info->flags;
771         int out_len = 0;
772         int word_len;
773         int rc = 0;
774         int got_bkupuid = 0;
775         int got_bkupgid = 0;
776         int got_uid = 0;
777         int got_cruid = 0;
778         int got_gid = 0;
779         uid_t uid, cruid = 0, bkupuid = 0;
780         gid_t gid, bkupgid = 0;
781         char *ep;
782         struct passwd *pw;
783         struct group *gr;
784         /*
785          * max 32-bit uint in decimal is 4294967295 which is 10 chars wide
786          * +1 for NULL, and +1 for good measure
787          */
788         char txtbuf[12];
789
790         /* make sure we're starting from beginning */
791         out[0] = '\0';
792
793         /* BB fixme check for separator override BB */
794         uid = getuid();
795         if (uid != 0)
796                 got_uid = 1;
797
798         gid = getgid();
799         if (gid != 0)
800                 got_gid = 1;
801
802         if (!data)
803                 return EX_USAGE;
804
805         /*
806          * format is keyword,keyword2=value2,keyword3=value3... 
807          * data  = next keyword
808          * value = next value ie stuff after equal sign
809          */
810         while (data && *data) {
811                 next_keyword = strchr(data, ',');       /* BB handle sep= */
812
813                 /* temporarily null terminate end of keyword=value pair */
814                 if (next_keyword)
815                         *next_keyword++ = 0;
816
817                 /* temporarily null terminate keyword if there's a value */
818                 value = NULL;
819                 if ((equals = strchr(data, '=')) != NULL) {
820                         *equals = '\0';
821                         value = equals + 1;
822                 }
823
824                 switch(parse_opt_token(data)) {
825                 case OPT_USERS:
826                         if (!value || !*value) {
827                                 *filesys_flags |= MS_USERS;
828                                 goto nocopy;
829                         }
830                         break;
831
832                 case OPT_USER:
833                         if (!value || !*value) {
834                                 if (data[4] == '\0') {
835                                         *filesys_flags |= MS_USER;
836                                         goto nocopy;
837                                 } else {
838                                         fprintf(stderr,
839                                                 "username specified with no parameter\n");
840                                         return EX_USAGE;
841                                 }
842                         } else {
843                                 strlcpy(parsed_info->username, value,
844                                         sizeof(parsed_info->username));
845                                 parsed_info->got_user = 1;
846                                 goto nocopy;
847                         }
848
849                 case OPT_PASS:
850                         if (parsed_info->got_password) {
851                                 fprintf(stderr,
852                                         "password specified twice, ignoring second\n");
853                                 goto nocopy;
854                         }
855                         if (!value || !*value) {
856                                 parsed_info->got_password = 1;
857                                 goto nocopy;
858                         }
859                         rc = set_password(parsed_info, value);
860                         if (rc)
861                                 return rc;
862                         goto nocopy;
863
864                 case OPT_SEC:
865                         if (value) {
866                                 if (!strncmp(value, "none", 4) ||
867                                     !strncmp(value, "krb5", 4))
868                                         parsed_info->got_password = 1;
869                         }
870                         break;
871
872                 case OPT_IP:
873                         if (!value || !*value) {
874                                 fprintf(stderr,
875                                         "target ip address argument missing\n");
876                         } else if (strnlen(value, MAX_ADDRESS_LEN) <=
877                                 MAX_ADDRESS_LEN) {
878                                 strcpy(parsed_info->addrlist, value);
879                                 if (parsed_info->verboseflag)
880                                         fprintf(stderr,
881                                                 "ip address %s override specified\n",
882                                                 value);
883                                 goto nocopy;
884                         } else {
885                                 fprintf(stderr, "ip address too long\n");
886                                 return EX_USAGE;
887
888                         }
889                         break;
890
891                 /* unc || target || path */
892                 case OPT_UNC:
893                         if (!value || !*value) {
894                                 fprintf(stderr,
895                                         "invalid path to network resource\n");
896                                 return EX_USAGE;
897                         }
898                         rc = parse_unc(value, parsed_info);
899                         if (rc)
900                                 return rc;
901                         break;
902
903                 /* dom || workgroup */
904                 case OPT_DOM:
905                         if (!value || !*value) {
906                                 fprintf(stderr, "CIFS: invalid domain name\n");
907                                 return EX_USAGE;
908                         }
909                         if (strnlen(value, sizeof(parsed_info->domain)) >=
910                             sizeof(parsed_info->domain)) {
911                                 fprintf(stderr, "domain name too long\n");
912                                 return EX_USAGE;
913                         }
914                         strlcpy(parsed_info->domain, value,
915                                 sizeof(parsed_info->domain));
916                         goto nocopy;
917
918                 case OPT_CRED:
919                         if (!value || !*value) {
920                                 fprintf(stderr,
921                                         "invalid credential file name specified\n");
922                                 return EX_USAGE;
923                         }
924                         rc = open_cred_file(value, parsed_info);
925                         if (rc) {
926                                 fprintf(stderr,
927                                         "error %d (%s) opening credential file %s\n",
928                                         rc, strerror(rc), value);
929                                 return rc;
930                         }
931                         goto nocopy;
932
933                 case OPT_UID:
934                         if (!value || !*value)
935                                 goto nocopy;
936
937                         got_uid = 1;
938                         pw = getpwnam(value);
939                         if (pw) {
940                                 uid = pw->pw_uid;
941                                 goto nocopy;
942                         }
943
944                         errno = 0;
945                         uid = strtoul(value, &ep, 10);
946                         if (errno == 0 && *ep == '\0')
947                                 goto nocopy;
948
949                         fprintf(stderr, "bad option uid=\"%s\"\n", value);
950                         return EX_USAGE;
951                 case OPT_CRUID:
952                         if (!value || !*value)
953                                 goto nocopy;
954
955                         got_cruid = 1;
956                         pw = getpwnam(value);
957                         if (pw) {
958                                 cruid = pw->pw_uid;
959                                 goto nocopy;
960                         }
961
962                         errno = 0;
963                         cruid = strtoul(value, &ep, 10);
964                         if (errno == 0 && *ep == '\0')
965                                 goto nocopy;
966
967                         fprintf(stderr, "bad option: cruid=\"%s\"\n", value);
968                         return EX_USAGE;
969                 case OPT_GID:
970                         if (!value || !*value)
971                                 goto nocopy;
972
973                         got_gid = 1;
974                         gr = getgrnam(value);
975                         if (gr) {
976                                 gid = gr->gr_gid;
977                                 goto nocopy;
978                         }
979
980                         errno = 0;
981                         gid = strtoul(value, &ep, 10);
982                         if (errno == 0 && *ep == '\0')
983                                 goto nocopy;
984
985                         fprintf(stderr, "bad option: gid=\"%s\"\n", value);
986                         return EX_USAGE;
987                 /* fmask falls through to file_mode */
988                 case OPT_FMASK:
989                         fprintf(stderr,
990                                 "WARNING: CIFS mount option 'fmask' is\
991                                  deprecated. Use 'file_mode' instead.\n");
992                         data = "file_mode";     /* BB fix this */
993                 case OPT_FILE_MODE:
994                         if (!value || !*value) {
995                                 fprintf(stderr,
996                                         "Option '%s' requires a numerical argument\n",
997                                         data);
998                                 return EX_USAGE;
999                         }
1000
1001                         if (value[0] != '0')
1002                                 fprintf(stderr,
1003                                         "WARNING: '%s' not expressed in octal.\n",
1004                                         data);
1005                         break;
1006
1007                 /* dmask falls through to dir_mode */
1008                 case OPT_DMASK:
1009                         fprintf(stderr,
1010                                 "WARNING: CIFS mount option 'dmask' is\
1011                                  deprecated. Use 'dir_mode' instead.\n");
1012                         data = "dir_mode";
1013                 case OPT_DIR_MODE:
1014                         if (!value || !*value) {
1015                                 fprintf(stderr,
1016                                         "Option '%s' requires a numerical argument\n",
1017                                         data);
1018                                 return EX_USAGE;
1019                         }
1020
1021                         if (value[0] != '0')
1022                                 fprintf(stderr,
1023                                         "WARNING: '%s' not expressed in octal.\n",
1024                                         data);
1025                         break;
1026                 case OPT_NO_SUID:
1027                         *filesys_flags |= MS_NOSUID;
1028                         goto nocopy;
1029                 case OPT_SUID:
1030                         *filesys_flags &= ~MS_NOSUID;
1031                         goto nocopy;
1032                 case OPT_NO_DEV:
1033                         *filesys_flags |= MS_NODEV;
1034                         goto nocopy;
1035                 case OPT_NO_LOCK:
1036                         *filesys_flags &= ~MS_MANDLOCK;
1037                         break;
1038                 case OPT_MAND:
1039                         *filesys_flags |= MS_MANDLOCK;
1040                         goto nocopy;
1041                 case OPT_NOMAND:
1042                         *filesys_flags &= ~MS_MANDLOCK;
1043                         goto nocopy;
1044                 case OPT_DEV:
1045                         *filesys_flags &= ~MS_NODEV;
1046                         goto nocopy;
1047                 case OPT_NO_EXEC:
1048                         *filesys_flags |= MS_NOEXEC;
1049                         goto nocopy;
1050                 case OPT_EXEC:
1051                         *filesys_flags &= ~MS_NOEXEC;
1052                         goto nocopy;
1053                 case OPT_GUEST:
1054                         parsed_info->got_user = 1;
1055                         parsed_info->got_password = 1;
1056                         goto nocopy;
1057                 case OPT_RO:
1058                         *filesys_flags |= MS_RDONLY;
1059                         goto nocopy;
1060                 case OPT_RW:
1061                         *filesys_flags &= ~MS_RDONLY;
1062                         goto nocopy;
1063                 case OPT_REMOUNT:
1064                         *filesys_flags |= MS_REMOUNT;
1065                         goto nocopy;
1066                 case OPT_IGNORE:
1067                         goto nocopy;
1068                 case OPT_BKUPUID:
1069                         if (!value || !*value)
1070                                 goto nocopy;
1071
1072                         got_bkupuid = 1;
1073                         errno = 0;
1074                         bkupuid = strtoul(value, &ep, 10);
1075                         if (errno == 0 && *ep == '\0')
1076                                 goto nocopy;
1077
1078                         pw = getpwnam(value);
1079                         if (pw == NULL) {
1080                                 fprintf(stderr,
1081                                         "bad user name \"%s\"\n", value);
1082                                 return EX_USAGE;
1083                         }
1084
1085                         bkupuid = pw->pw_uid;
1086                         goto nocopy;
1087                 case OPT_BKUPGID:
1088                         if (!value || !*value)
1089                                 goto nocopy;
1090
1091                         got_bkupgid = 1;
1092                         errno = 0;
1093                         bkupgid = strtoul(value, &ep, 10);
1094                         if (errno == 0 && *ep == '\0')
1095                                 goto nocopy;
1096
1097                         gr = getgrnam(value);
1098                         if (gr == NULL) {
1099                                 fprintf(stderr,
1100                                         "bad group name \"%s\"\n", value);
1101                                 return EX_USAGE;
1102                         }
1103
1104                         bkupgid = gr->gr_gid;
1105                         goto nocopy;
1106                 case OPT_NOFAIL:
1107                         parsed_info->nofail = 1;
1108                         goto nocopy;
1109                 }
1110
1111                 /* check size before copying option to buffer */
1112                 word_len = strlen(data);
1113                 if (value)
1114                         word_len += 1 + strlen(value);
1115
1116                 /* need 2 extra bytes for comma and null byte */
1117                 if (out_len + word_len + 2 > MAX_OPTIONS_LEN) {
1118                         fprintf(stderr, "Options string too long\n");
1119                         return EX_USAGE;
1120                 }
1121
1122                 /* put back equals sign, if any */
1123                 if (equals)
1124                         *equals = '=';
1125
1126                 /* go ahead and copy */
1127                 if (out_len)
1128                         strlcat(out, ",", MAX_OPTIONS_LEN);
1129
1130                 strlcat(out, data, MAX_OPTIONS_LEN);
1131                 out_len = strlen(out);
1132 nocopy:
1133                 data = next_keyword;
1134         }
1135
1136
1137         /* special-case the uid and gid */
1138         if (got_uid) {
1139                 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", uid);
1140
1141                 /* comma + "uid=" + terminating NULL == 6 */
1142                 if (out_len + word_len + 6 > MAX_OPTIONS_LEN) {
1143                         fprintf(stderr, "Options string too long\n");
1144                         return EX_USAGE;
1145                 }
1146
1147                 if (out_len) {
1148                         strlcat(out, ",", MAX_OPTIONS_LEN);
1149                         out_len++;
1150                 }
1151                 snprintf(out + out_len, word_len + 5, "uid=%s", txtbuf);
1152                 out_len = strlen(out);
1153         }
1154         if (got_cruid) {
1155                 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", cruid);
1156
1157                 /* comma + "cruid=" + terminating NULL == 8 */
1158                 if (out_len + word_len + 8 > MAX_OPTIONS_LEN) {
1159                         fprintf(stderr, "Options string too long\n");
1160                         return EX_USAGE;
1161                 }
1162
1163                 if (out_len) {
1164                         strlcat(out, ",", MAX_OPTIONS_LEN);
1165                         out_len++;
1166                 }
1167                 snprintf(out + out_len, word_len + 7, "cruid=%s", txtbuf);
1168                 out_len = strlen(out);
1169         }
1170         if (got_gid) {
1171                 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", gid);
1172
1173                 /* comma + "gid=" + terminating NULL == 6 */
1174                 if (out_len + word_len + 6 > MAX_OPTIONS_LEN) {
1175                         fprintf(stderr, "Options string too long\n");
1176                         return EX_USAGE;
1177                 }
1178
1179                 if (out_len) {
1180                         strlcat(out, ",", MAX_OPTIONS_LEN);
1181                         out_len++;
1182                 }
1183                 snprintf(out + out_len, word_len + 5, "gid=%s", txtbuf);
1184         }
1185         if (got_bkupuid) {
1186                 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", bkupuid);
1187
1188                 /* comma + "backupuid=" + terminating NULL == 12 */
1189                 if (out_len + word_len + 12 > MAX_OPTIONS_LEN) {
1190                         fprintf(stderr, "Options string too long\n");
1191                         return EX_USAGE;
1192                 }
1193
1194                 if (out_len) {
1195                         strlcat(out, ",", MAX_OPTIONS_LEN);
1196                         out_len++;
1197                 }
1198                 snprintf(out + out_len, word_len + 11, "backupuid=%s", txtbuf);
1199                 out_len = strlen(out);
1200         }
1201         if (got_bkupgid) {
1202                 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", bkupgid);
1203
1204                 /* comma + "backkupgid=" + terminating NULL == 12 */
1205                 if (out_len + word_len + 12 > MAX_OPTIONS_LEN) {
1206                         fprintf(stderr, "Options string too long\n");
1207                         return EX_USAGE;
1208                 }
1209
1210                 if (out_len) {
1211                         strlcat(out, ",", MAX_OPTIONS_LEN);
1212                         out_len++;
1213                 }
1214                 snprintf(out + out_len, word_len + 11, "backupgid=%s", txtbuf);
1215         }
1216
1217         return 0;
1218 }
1219
1220 static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info)
1221 {
1222         int length = strnlen(unc_name, MAX_UNC_LEN);
1223         const char *host, *share, *prepath;
1224         size_t hostlen, sharelen, prepathlen;
1225
1226         if (length > (MAX_UNC_LEN - 1)) {
1227                 fprintf(stderr, "mount error: UNC name too long\n");
1228                 return EX_USAGE;
1229         }
1230
1231         if (length < 3) {
1232                 fprintf(stderr, "mount error: UNC name too short\n");
1233                 return EX_USAGE;
1234         }
1235
1236         if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1237             (strncasecmp("smb://", unc_name, 6) == 0)) {
1238                 fprintf(stderr,
1239                         "Mounting cifs URL not implemented yet. Attempt to mount %s\n",
1240                         unc_name);
1241                 return EX_USAGE;
1242         }
1243
1244         if (strncmp(unc_name, "//", 2) && strncmp(unc_name, "\\\\", 2)) {
1245                 fprintf(stderr, "mount.cifs: bad UNC (%s)\n", unc_name);
1246                 return EX_USAGE;
1247         }
1248
1249         host = unc_name + 2;
1250         hostlen = strcspn(host, "/\\");
1251         if (!hostlen) {
1252                 fprintf(stderr, "mount.cifs: bad UNC (%s)\n", unc_name);
1253                 return EX_USAGE;
1254         }
1255         share = host + hostlen + 1;
1256
1257         if (hostlen + 1 > sizeof(parsed_info->host)) {
1258                 fprintf(stderr, "mount.cifs: host portion of UNC too long\n");
1259                 return EX_USAGE;
1260         }
1261
1262         sharelen = strcspn(share, "/\\");
1263         if (sharelen + 1 > sizeof(parsed_info->share)) {
1264                 fprintf(stderr, "mount.cifs: share portion of UNC too long\n");
1265                 return EX_USAGE;
1266         }
1267
1268         prepath = share + sharelen;
1269         if (*prepath != '\0')
1270                 prepath++;
1271
1272         prepathlen = strlen(prepath);
1273
1274         if (prepathlen + 1 > sizeof(parsed_info->prefix)) {
1275                 fprintf(stderr, "mount.cifs: UNC prefixpath too long\n");
1276                 return EX_USAGE;
1277         }
1278
1279         /* copy pieces into their resepective buffers */
1280         memcpy(parsed_info->host, host, hostlen);
1281         memcpy(parsed_info->share, share, sharelen);
1282         memcpy(parsed_info->prefix, prepath, prepathlen);
1283
1284         return 0;
1285 }
1286
1287 static int get_pw_from_env(struct parsed_mount_info *parsed_info)
1288 {
1289         int rc = 0;
1290
1291         if (getenv("PASSWD"))
1292                 rc = set_password(parsed_info, getenv("PASSWD"));
1293         else if (getenv("PASSWD_FD"))
1294                 rc = get_password_from_file(atoi(getenv("PASSWD_FD")), NULL,
1295                                             parsed_info);
1296         else if (getenv("PASSWD_FILE"))
1297                 rc = get_password_from_file(0, getenv("PASSWD_FILE"),
1298                                             parsed_info);
1299
1300         return rc;
1301 }
1302
1303 static struct option longopts[] = {
1304         {"all", 0, NULL, 'a'},
1305         {"help", 0, NULL, 'h'},
1306         {"move", 0, NULL, 'm'},
1307         {"bind", 0, NULL, 'b'},
1308         {"read-only", 0, NULL, 'r'},
1309         {"ro", 0, NULL, 'r'},
1310         {"verbose", 0, NULL, 'v'},
1311         {"version", 0, NULL, 'V'},
1312         {"read-write", 0, NULL, 'w'},
1313         {"rw", 0, NULL, 'w'},
1314         {"options", 1, NULL, 'o'},
1315         {"type", 1, NULL, 't'},
1316         {"uid", 1, NULL, '1'},
1317         {"gid", 1, NULL, '2'},
1318         {"user", 1, NULL, 'u'},
1319         {"username", 1, NULL, 'u'},
1320         {"dom", 1, NULL, 'd'},
1321         {"domain", 1, NULL, 'd'},
1322         {"password", 1, NULL, 'p'},
1323         {"pass", 1, NULL, 'p'},
1324         {"credentials", 1, NULL, 'c'},
1325         {"port", 1, NULL, 'P'},
1326         {"sloppy", 0, NULL, 's'},
1327         {NULL, 0, NULL, 0}
1328 };
1329
1330 /* convert a string to uppercase. return false if the string
1331  * wasn't ASCII. Return success on a NULL ptr */
1332 static int uppercase_string(char *string)
1333 {
1334         if (!string)
1335                 return 1;
1336
1337         while (*string) {
1338                 /* check for unicode */
1339                 if ((unsigned char)string[0] & 0x80)
1340                         return 0;
1341                 *string = toupper((unsigned char)*string);
1342                 string++;
1343         }
1344
1345         return 1;
1346 }
1347
1348 static void print_cifs_mount_version(void)
1349 {
1350         printf("mount.cifs version: %s\n", VERSION);
1351 }
1352
1353 /*
1354  * This function borrowed from fuse-utils...
1355  *
1356  * glibc's addmntent (at least as of 2.10 or so) doesn't properly encode
1357  * newlines embedded within the text fields. To make sure no one corrupts
1358  * the mtab, fail the mount if there are embedded newlines.
1359  */
1360 static int check_newline(const char *progname, const char *name)
1361 {
1362         const char *s;
1363         for (s = "\n"; *s; s++) {
1364                 if (strchr(name, *s)) {
1365                         fprintf(stderr,
1366                                 "%s: illegal character 0x%02x in mount entry\n",
1367                                 progname, *s);
1368                         return EX_USAGE;
1369                 }
1370         }
1371         return 0;
1372 }
1373
1374 static int check_mtab(const char *progname, const char *devname,
1375                       const char *dir)
1376 {
1377         if (check_newline(progname, devname) || check_newline(progname, dir))
1378                 return EX_USAGE;
1379         return 0;
1380 }
1381
1382 static int
1383 add_mtab(char *devname, char *mountpoint, unsigned long flags, const char *fstype)
1384 {
1385         int rc = 0, tmprc, fd;
1386         uid_t uid;
1387         char *mount_user = NULL;
1388         struct mntent mountent;
1389         struct stat statbuf;
1390         FILE *pmntfile;
1391         sigset_t mask, oldmask;
1392
1393         uid = getuid();
1394         if (uid != 0)
1395                 mount_user = getusername(uid);
1396
1397         /*
1398          * Set the real uid to the effective uid. This prevents unprivileged
1399          * users from sending signals to this process, though ^c on controlling
1400          * terminal should still work.
1401          */
1402         rc = setreuid(geteuid(), -1);
1403         if (rc != 0) {
1404                 fprintf(stderr, "Unable to set real uid to effective uid: %s\n",
1405                                 strerror(errno));
1406                 return EX_FILEIO;
1407         }
1408
1409         rc = sigfillset(&mask);
1410         if (rc) {
1411                 fprintf(stderr, "Unable to set filled signal mask\n");
1412                 return EX_FILEIO;
1413         }
1414
1415         rc = sigprocmask(SIG_SETMASK, &mask, &oldmask);
1416         if (rc) {
1417                 fprintf(stderr, "Unable to make process ignore signals\n");
1418                 return EX_FILEIO;
1419         }
1420
1421         rc = toggle_dac_capability(1, 1);
1422         if (rc)
1423                 return EX_FILEIO;
1424
1425         atexit(unlock_mtab);
1426         rc = lock_mtab();
1427         if (rc) {
1428                 fprintf(stderr, "cannot lock mtab");
1429                 rc = EX_FILEIO;
1430                 goto add_mtab_exit;
1431         }
1432
1433         pmntfile = setmntent(MOUNTED, "a+");
1434         if (!pmntfile) {
1435                 fprintf(stderr, "could not update mount table\n");
1436                 unlock_mtab();
1437                 rc = EX_FILEIO;
1438                 goto add_mtab_exit;
1439         }
1440
1441         fd = fileno(pmntfile);
1442         if (fd < 0) {
1443                 fprintf(stderr, "mntent does not appear to be valid\n");
1444                 unlock_mtab();
1445                 rc = EX_FILEIO;
1446                 goto add_mtab_exit;
1447         }
1448
1449         rc = fstat(fd, &statbuf);
1450         if (rc != 0) {
1451                 fprintf(stderr, "unable to fstat open mtab\n");
1452                 endmntent(pmntfile);
1453                 unlock_mtab();
1454                 rc = EX_FILEIO;
1455                 goto add_mtab_exit;
1456         }
1457
1458         mountent.mnt_fsname = devname;
1459         mountent.mnt_dir = mountpoint;
1460         mountent.mnt_type = (char *)(void *)fstype;
1461         mountent.mnt_opts = (char *)calloc(MTAB_OPTIONS_LEN, 1);
1462         if (mountent.mnt_opts) {
1463                 if (flags & MS_RDONLY)
1464                         strlcat(mountent.mnt_opts, "ro", MTAB_OPTIONS_LEN);
1465                 else
1466                         strlcat(mountent.mnt_opts, "rw", MTAB_OPTIONS_LEN);
1467
1468                 if (flags & MS_MANDLOCK)
1469                         strlcat(mountent.mnt_opts, ",mand", MTAB_OPTIONS_LEN);
1470                 if (flags & MS_NOEXEC)
1471                         strlcat(mountent.mnt_opts, ",noexec", MTAB_OPTIONS_LEN);
1472                 if (flags & MS_NOSUID)
1473                         strlcat(mountent.mnt_opts, ",nosuid", MTAB_OPTIONS_LEN);
1474                 if (flags & MS_NODEV)
1475                         strlcat(mountent.mnt_opts, ",nodev", MTAB_OPTIONS_LEN);
1476                 if (flags & MS_SYNCHRONOUS)
1477                         strlcat(mountent.mnt_opts, ",sync", MTAB_OPTIONS_LEN);
1478                 if (mount_user) {
1479                         strlcat(mountent.mnt_opts, ",user=", MTAB_OPTIONS_LEN);
1480                         strlcat(mountent.mnt_opts, mount_user,
1481                                 MTAB_OPTIONS_LEN);
1482                 }
1483         }
1484         mountent.mnt_freq = 0;
1485         mountent.mnt_passno = 0;
1486         rc = addmntent(pmntfile, &mountent);
1487         if (rc) {
1488                 int ignore __attribute__((unused));
1489
1490                 fprintf(stderr, "unable to add mount entry to mtab\n");
1491                 ignore = ftruncate(fd, statbuf.st_size);
1492                 rc = EX_FILEIO;
1493         }
1494         tmprc = my_endmntent(pmntfile, statbuf.st_size);
1495         if (tmprc) {
1496                 fprintf(stderr, "error %d detected on close of mtab\n", tmprc);
1497                 rc = EX_FILEIO;
1498         }
1499         unlock_mtab();
1500         SAFE_FREE(mountent.mnt_opts);
1501 add_mtab_exit:
1502         toggle_dac_capability(1, 0);
1503         sigprocmask(SIG_SETMASK, &oldmask, NULL);
1504
1505         return rc;
1506 }
1507
1508 static int
1509 del_mtab(char *mountpoint)
1510 {
1511         int len, tmprc, rc = 0;
1512         FILE *mnttmp, *mntmtab;
1513         struct mntent *mountent;
1514         char *mtabfile, *mtabdir, *mtabtmpfile = NULL;
1515
1516         mtabfile = strdup(MOUNTED);
1517         if (!mtabfile) {
1518                 fprintf(stderr, "del_mtab: cannot strdup MOUNTED\n");
1519                 rc = EX_FILEIO;
1520                 goto del_mtab_exit;
1521         }
1522
1523         mtabdir = dirname(mtabfile);
1524         len = strlen(mtabdir) + strlen(MNT_TMP_FILE);
1525         mtabtmpfile = malloc(len + 1);
1526         if (!mtabtmpfile) {
1527                 fprintf(stderr, "del_mtab: cannot allocate memory to tmp file\n");
1528                 rc = EX_FILEIO;
1529                 goto del_mtab_exit;
1530         }
1531
1532         if (sprintf(mtabtmpfile, "%s%s", mtabdir, MNT_TMP_FILE) != len) {
1533                 fprintf(stderr, "del_mtab: error writing new string\n");
1534                 rc = EX_FILEIO;
1535                 goto del_mtab_exit;
1536         }
1537
1538         atexit(unlock_mtab);
1539         rc = lock_mtab();
1540         if (rc) {
1541                 fprintf(stderr, "del_mtab: cannot lock mtab\n");
1542                 rc = EX_FILEIO;
1543                 goto del_mtab_exit;
1544         }
1545
1546         mtabtmpfile = mktemp(mtabtmpfile);
1547         if (!mtabtmpfile) {
1548                 fprintf(stderr, "del_mtab: cannot setup tmp file destination\n");
1549                 rc = EX_FILEIO;
1550                 goto del_mtab_exit;
1551         }
1552
1553         mntmtab = setmntent(MOUNTED, "r");
1554         if (!mntmtab) {
1555                 fprintf(stderr, "del_mtab: could not update mount table\n");
1556                 rc = EX_FILEIO;
1557                 goto del_mtab_exit;
1558         }
1559
1560         mnttmp = setmntent(mtabtmpfile, "w");
1561         if (!mnttmp) {
1562                 fprintf(stderr, "del_mtab: could not update mount table\n");
1563                 endmntent(mntmtab);
1564                 rc = EX_FILEIO;
1565                 goto del_mtab_exit;
1566         }
1567
1568         while ((mountent = getmntent(mntmtab)) != NULL) {
1569                 if (!strcmp(mountent->mnt_dir, mountpoint))
1570                         continue;
1571                 rc = addmntent(mnttmp, mountent);
1572                 if (rc) {
1573                         fprintf(stderr, "del_mtab: unable to add mount entry to mtab\n");
1574                         rc = EX_FILEIO;
1575                         goto del_mtab_error;
1576                 }
1577         }
1578
1579         endmntent(mntmtab);
1580
1581         tmprc = my_endmntent(mnttmp, 0);
1582         if (tmprc) {
1583                 fprintf(stderr, "del_mtab: error %d detected on close of tmp file\n", tmprc);
1584                 rc = EX_FILEIO;
1585                 goto del_mtab_error;
1586         }
1587
1588         if (rename(mtabtmpfile, MOUNTED)) {
1589                 fprintf(stderr, "del_mtab: error %d when renaming mtab in place\n", errno);
1590                 rc = EX_FILEIO;
1591                 goto del_mtab_error;
1592         }
1593
1594 del_mtab_exit:
1595         unlock_mtab();
1596         free(mtabtmpfile);
1597         free(mtabfile);
1598         return rc;
1599
1600 del_mtab_error:
1601         if (unlink(mtabtmpfile))
1602                 fprintf(stderr, "del_mtab: failed to delete tmp file - %s\n",
1603                                 strerror(errno));
1604         goto del_mtab_exit;
1605 }
1606
1607 /* have the child drop root privileges */
1608 static int
1609 drop_child_privs(void)
1610 {
1611         int rc;
1612         uid_t uid = getuid();
1613         gid_t gid = getgid();
1614
1615         if (gid) {
1616                 rc = setgid(gid);
1617                 if (rc) {
1618                         fprintf(stderr, "Unable set group identity: %s\n",
1619                                         strerror(errno));
1620                         return EX_SYSERR;
1621                 }
1622         }
1623         if (uid) {
1624                 rc = setuid(uid);
1625                 if (rc) {
1626                         fprintf(stderr, "Unable set user identity: %s\n",
1627                                         strerror(errno));
1628                         return EX_SYSERR;
1629                 }
1630         }
1631
1632         return 0;
1633 }
1634
1635 /*
1636  * If systemd is running and systemd-ask-password --
1637  * is available, then use that else fallback on getpass(..)
1638  *
1639  * Returns: @input or NULL on error
1640  */
1641 static char*
1642 get_password(const char *prompt, char *input, int capacity)
1643 {
1644 #ifdef ENABLE_SYSTEMD
1645         int is_systemd_running;
1646         struct stat a, b;
1647
1648         /* We simply test whether the systemd cgroup hierarchy is
1649          * mounted */
1650         is_systemd_running = (lstat("/sys/fs/cgroup", &a) == 0)
1651                 && (lstat("/sys/fs/cgroup/systemd", &b) == 0)
1652                 && (a.st_dev != b.st_dev);
1653
1654         if (is_systemd_running) {
1655                 char *cmd, *ret;
1656                 FILE *ask_pass_fp = NULL;
1657
1658                 cmd = ret = NULL;
1659                 if (asprintf(&cmd, "systemd-ask-password \"%s\"", prompt) >= 0) {
1660                         ask_pass_fp = popen (cmd, "re");
1661                         free (cmd);
1662                 }
1663
1664                 if (ask_pass_fp) {
1665                         ret = fgets(input, capacity, ask_pass_fp);
1666                         pclose(ask_pass_fp);
1667                 }
1668
1669                 if (ret) {
1670                         int len = strlen(input);
1671                         if (input[len - 1] == '\n')
1672                                 input[len - 1] = '\0';
1673                         return input;
1674                 }
1675         }
1676 #endif
1677
1678         /*
1679          * Falling back to getpass(..)
1680          * getpass is obsolete, but there's apparently nothing that replaces it
1681          */
1682         char *tmp_pass = getpass(prompt);
1683         if (!tmp_pass)
1684                 return NULL;
1685
1686         strncpy(input, tmp_pass, capacity - 1);
1687         input[capacity - 1] = '\0';
1688
1689         /* zero-out the static buffer */
1690         memset(tmp_pass, 0, strlen(tmp_pass));
1691
1692         return input;
1693 }
1694
1695 static int
1696 assemble_mountinfo(struct parsed_mount_info *parsed_info,
1697                    const char *thisprogram, const char *mountpoint,
1698                    const char *orig_dev, char *orgoptions)
1699 {
1700         int rc;
1701
1702         rc = drop_capabilities(0);
1703         if (rc)
1704                 goto assemble_exit;
1705
1706         rc = drop_child_privs();
1707         if (rc)
1708                 goto assemble_exit;
1709
1710         if (getuid()) {
1711                 rc = check_fstab(thisprogram, mountpoint, orig_dev,
1712                                  &orgoptions);
1713                 if (rc)
1714                         goto assemble_exit;
1715
1716                 /* enable any default user mount flags */
1717                 parsed_info->flags |= CIFS_SETUID_FLAGS;
1718         }
1719
1720         rc = get_pw_from_env(parsed_info);
1721         if (rc)
1722                 goto assemble_exit;
1723
1724         if (orgoptions) {
1725                 rc = parse_options(orgoptions, parsed_info);
1726                 if (rc)
1727                         goto assemble_exit;
1728         }
1729
1730         if (getuid()) {
1731                 if (!(parsed_info->flags & (MS_USERS | MS_USER))) {
1732                         fprintf(stderr, "%s: permission denied\n", thisprogram);
1733                         rc = EX_USAGE;
1734                         goto assemble_exit;
1735                 }
1736         }
1737
1738         parsed_info->flags &= ~(MS_USERS | MS_USER);
1739
1740         rc = parse_unc(orig_dev, parsed_info);
1741         if (rc)
1742                 goto assemble_exit;
1743
1744         if (parsed_info->addrlist[0] == '\0')
1745                 rc = resolve_host(parsed_info->host, parsed_info->addrlist);
1746
1747         switch (rc) {
1748         case EX_USAGE:
1749                 fprintf(stderr, "mount error: could not resolve address for "
1750                         "%s: %s\n", parsed_info->host,
1751                         rc == EAI_SYSTEM ? strerror(errno) : gai_strerror(rc));
1752                 goto assemble_exit;
1753
1754         case EX_SYSERR:
1755                 fprintf(stderr, "mount error: problem parsing address "
1756                         "list: %s\n", strerror(errno));
1757                 goto assemble_exit;
1758         }
1759
1760         if (!parsed_info->got_user) {
1761                 /*
1762                  * Note that the password will not be retrieved from the
1763                  * USER env variable (ie user%password form) as there is
1764                  * already a PASSWD environment varaible
1765                  */
1766                 if (getenv("USER"))
1767                         strlcpy(parsed_info->username, getenv("USER"),
1768                                 sizeof(parsed_info->username));
1769                 else
1770                         strlcpy(parsed_info->username, getusername(getuid()),
1771                                 sizeof(parsed_info->username));
1772                 parsed_info->got_user = 1;
1773         }
1774
1775         if (!parsed_info->got_password) {
1776                 char tmp_pass[MOUNT_PASSWD_SIZE + 1];
1777                 char *prompt = NULL;
1778
1779                 if(asprintf(&prompt, "Password for %s@%s: ", parsed_info->username, orig_dev) < 0)
1780                         prompt = NULL;
1781
1782                 if (get_password(prompt ? prompt : "Password: ", tmp_pass, MOUNT_PASSWD_SIZE + 1)) {
1783                         rc = set_password(parsed_info, tmp_pass);
1784                 } else {
1785                         fprintf(stderr, "Error reading password, exiting\n");
1786                         rc = EX_SYSERR;
1787                 }
1788
1789                 free(prompt);
1790                 if (rc)
1791                         goto assemble_exit;
1792         }
1793
1794         /* copy in user= string */
1795         if (parsed_info->got_user) {
1796                 if (*parsed_info->options)
1797                         strlcat(parsed_info->options, ",",
1798                                 sizeof(parsed_info->options));
1799                 strlcat(parsed_info->options, "user=",
1800                         sizeof(parsed_info->options));
1801                 strlcat(parsed_info->options, parsed_info->username,
1802                         sizeof(parsed_info->options));
1803         }
1804
1805         if (*parsed_info->domain) {
1806                 if (*parsed_info->options)
1807                         strlcat(parsed_info->options, ",",
1808                                 sizeof(parsed_info->options));
1809                 strlcat(parsed_info->options, ",domain=",
1810                         sizeof(parsed_info->options));
1811                 strlcat(parsed_info->options, parsed_info->domain,
1812                         sizeof(parsed_info->options));
1813         }
1814
1815 assemble_exit:
1816         return rc;
1817 }
1818
1819 /*
1820  * chdir() into the mountpoint and determine "realpath". We assume here that
1821  * "mountpoint" is a statically allocated string and does not need to be freed.
1822  */
1823 static int
1824 acquire_mountpoint(char **mountpointp)
1825 {
1826         int rc, dacrc;
1827         uid_t realuid, oldfsuid;
1828         gid_t oldfsgid;
1829         char *mountpoint;
1830
1831         /*
1832          * Acquire the necessary privileges to chdir to the mountpoint. If
1833          * the real uid is root, then we reacquire CAP_DAC_READ_SEARCH. If
1834          * it's not, then we change the fsuid to the real uid to ensure that
1835          * the mounting user actually has access to the mountpoint.
1836          *
1837          * The mount(8) manpage does not state that users must be able to
1838          * chdir into the mountpoint in order to mount onto it, but if we
1839          * allow that, then an unprivileged user could use this program to
1840          * "probe" into directories to which he does not have access.
1841          */
1842         realuid = getuid();
1843         if (realuid == 0) {
1844                 dacrc = toggle_dac_capability(0, 1);
1845                 if (dacrc)
1846                         return dacrc;
1847         } else {
1848                 oldfsuid = setfsuid(realuid);
1849                 oldfsgid = setfsgid(getgid());
1850         }
1851
1852         rc = chdir(*mountpointp);
1853         if (rc) {
1854                 fprintf(stderr, "Couldn't chdir to %s: %s\n", *mountpointp,
1855                         strerror(errno));
1856                 rc = EX_USAGE;
1857                 goto restore_privs;
1858         }
1859
1860         mountpoint = realpath(".", NULL);
1861         if (!mountpoint) {
1862                 fprintf(stderr, "Unable to resolve %s to canonical path: %s\n",
1863                         *mountpointp, strerror(errno));
1864                 rc = EX_SYSERR;
1865         }
1866
1867         *mountpointp = mountpoint;
1868 restore_privs:
1869         if (realuid == 0) {
1870                 dacrc = toggle_dac_capability(0, 0);
1871                 if (dacrc)
1872                         rc = rc ? rc : dacrc;
1873         } else {
1874                 uid_t __attribute__((unused)) uignore = setfsuid(oldfsuid);
1875                 gid_t __attribute__((unused)) gignore = setfsgid(oldfsgid);
1876         }
1877
1878         return rc;
1879 }
1880
1881 int main(int argc, char **argv)
1882 {
1883         int c;
1884         char *orgoptions = NULL;
1885         char *mountpoint = NULL;
1886         char *options = NULL;
1887         char *orig_dev = NULL;
1888         char *currentaddress, *nextaddress;
1889         int rc = 0;
1890         int already_uppercased = 0;
1891         int sloppy = 0;
1892         size_t options_size = MAX_OPTIONS_LEN;
1893         struct parsed_mount_info *parsed_info = NULL;
1894         pid_t pid;
1895
1896         rc = check_setuid();
1897         if (rc)
1898                 return rc;
1899
1900         rc = drop_capabilities(1);
1901         if (rc)
1902                 return EX_SYSERR;
1903
1904         /* setlocale(LC_ALL, "");
1905            bindtextdomain(PACKAGE, LOCALEDIR);
1906            textdomain(PACKAGE); */
1907
1908         if (!argc || !argv) {
1909                 rc = mount_usage(stderr);
1910                 goto mount_exit;
1911         }
1912
1913         thisprogram = basename(argv[0]);
1914         if (thisprogram == NULL)
1915                 thisprogram = "mount.cifs";
1916
1917         /* allocate parsed_info as shared anonymous memory range */
1918         parsed_info = mmap((void *)0, sizeof(*parsed_info), PROT_READ | PROT_WRITE,
1919                            MAP_ANONYMOUS | MAP_SHARED, -1, 0);
1920         if (parsed_info == (struct parsed_mount_info *) -1) {
1921                 parsed_info = NULL;
1922                 fprintf(stderr, "Unable to allocate memory: %s\n",
1923                                 strerror(errno));
1924                 return EX_SYSERR;
1925         }
1926
1927         /* add sharename in opts string as unc= parm */
1928         while ((c = getopt_long(argc, argv, "?fhno:rsvVw",
1929                                 longopts, NULL)) != -1) {
1930                 switch (c) {
1931                 case '?':
1932                 case 'h':       /* help */
1933                         rc = mount_usage(stdout);
1934                         goto mount_exit;
1935                 case 'n':
1936                         ++parsed_info->nomtab;
1937                         break;
1938                 case 'o':
1939                         orgoptions = strndup(optarg, MAX_OPTIONS_LEN);
1940                         if (!orgoptions) {
1941                                 rc = EX_SYSERR;
1942                                 goto mount_exit;
1943                         }
1944                         break;
1945                 case 'r':       /* mount readonly */
1946                         parsed_info->flags |= MS_RDONLY;
1947                         break;
1948                 case 'v':
1949                         ++parsed_info->verboseflag;
1950                         break;
1951                 case 'V':
1952                         print_cifs_mount_version();
1953                         exit(0);
1954                 case 'w':
1955                         parsed_info->flags &= ~MS_RDONLY;
1956                         break;
1957                 case 'f':
1958                         ++parsed_info->fakemnt;
1959                         break;
1960                 case 's':
1961                         ++sloppy;
1962                         break;
1963                 default:
1964                         fprintf(stderr, "unknown command-line option: %c\n", c);
1965                         rc = mount_usage(stderr);
1966                         goto mount_exit;
1967                 }
1968         }
1969
1970         if (argc < optind + 2) {
1971                 rc = mount_usage(stderr);
1972                 goto mount_exit;
1973         }
1974
1975         orig_dev = argv[optind];
1976         mountpoint = argv[optind + 1];
1977
1978         /* chdir into mountpoint as soon as possible */
1979         rc = acquire_mountpoint(&mountpoint);
1980         if (rc)
1981                 return rc;
1982
1983         /*
1984          * mount.cifs does privilege separation. Most of the code to handle
1985          * assembling the mount info is done in a child process that drops
1986          * privileges. The info is assembled in parsed_info which is a
1987          * shared, mmaped memory segment. The parent waits for the child to
1988          * exit and checks the return code. If it's anything but "0", then
1989          * the process exits without attempting anything further.
1990          */
1991         pid = fork();
1992         if (pid == -1) {
1993                 fprintf(stderr, "Unable to fork: %s\n", strerror(errno));
1994                 rc = EX_SYSERR;
1995                 goto mount_exit;
1996         } else if (!pid) {
1997                 /* child */
1998                 rc = assemble_mountinfo(parsed_info, thisprogram, mountpoint,
1999                                         orig_dev, orgoptions);
2000                 return rc;
2001         } else {
2002                 /* parent */
2003                 pid = wait(&rc);
2004                 if (!WIFEXITED(rc)) {
2005                         fprintf(stderr, "Child process terminated abnormally.\n");
2006                         rc = EX_SYSERR;
2007                         goto mount_exit;
2008                 }
2009                 rc = WEXITSTATUS(rc);
2010                 if (rc)
2011                         goto mount_exit;
2012         }
2013
2014         options = calloc(options_size, 1);
2015         if (!options) {
2016                 fprintf(stderr, "Unable to allocate memory.\n");
2017                 rc = EX_SYSERR;
2018                 goto mount_exit;
2019         }
2020
2021         currentaddress = parsed_info->addrlist;
2022         nextaddress = strchr(currentaddress, ',');
2023         if (nextaddress)
2024                 *nextaddress++ = '\0';
2025
2026 mount_retry:
2027         if (!currentaddress) {
2028                 fprintf(stderr, "Unable to find suitable address.\n");
2029                 rc = parsed_info->nofail ? 0 : EX_FAIL;
2030                 goto mount_exit;
2031         }
2032         strlcpy(options, "ip=", options_size);
2033         strlcat(options, currentaddress, options_size);
2034
2035         strlcat(options, ",unc=\\\\", options_size);
2036         strlcat(options, parsed_info->host, options_size);
2037         strlcat(options, "\\", options_size);
2038         strlcat(options, parsed_info->share, options_size);
2039
2040         if (*parsed_info->options) {
2041                 strlcat(options, ",", options_size);
2042                 strlcat(options, parsed_info->options, options_size);
2043         }
2044
2045         if (*parsed_info->prefix) {
2046                 strlcat(options, ",prefixpath=", options_size);
2047                 strlcat(options, parsed_info->prefix, options_size);
2048         }
2049
2050         if (sloppy)
2051                 strlcat(options, ",sloppy", options_size);
2052
2053         if (parsed_info->verboseflag)
2054                 fprintf(stderr, "%s kernel mount options: %s",
2055                         thisprogram, options);
2056
2057         if (parsed_info->got_password) {
2058                 /*
2059                  * Commas have to be doubled, or else they will
2060                  * look like the parameter separator
2061                  */
2062                 strlcat(options, ",pass=", options_size);
2063                 strlcat(options, parsed_info->password, options_size);
2064                 if (parsed_info->verboseflag)
2065                         fprintf(stderr, ",pass=********");
2066         }
2067
2068         if (parsed_info->verboseflag)
2069                 fprintf(stderr, "\n");
2070
2071         rc = check_mtab(thisprogram, orig_dev, mountpoint);
2072         if (rc)
2073                 goto mount_exit;
2074
2075         if (!parsed_info->fakemnt) {
2076                 toggle_dac_capability(0, 1);
2077                 rc = mount(orig_dev, ".", cifs_fstype, parsed_info->flags, options);
2078                 toggle_dac_capability(0, 0);
2079                 if (rc == 0)
2080                         goto do_mtab;
2081
2082                 switch (errno) {
2083                 case ECONNREFUSED:
2084                 case EHOSTUNREACH:
2085                         currentaddress = nextaddress;
2086                         if (currentaddress) {
2087                                 nextaddress = strchr(currentaddress, ',');
2088                                 if (nextaddress)
2089                                         *nextaddress++ = '\0';
2090                         }
2091                         goto mount_retry;
2092                 case ENODEV:
2093                         fprintf(stderr,
2094                                 "mount error: %s filesystem not supported by the system\n", cifs_fstype);
2095                         break;
2096                 case ENXIO:
2097                         if (!already_uppercased &&
2098                             uppercase_string(parsed_info->host) &&
2099                             uppercase_string(parsed_info->share) &&
2100                             uppercase_string(parsed_info->prefix) &&
2101                             uppercase_string(orig_dev)) {
2102                                 fprintf(stderr,
2103                                         "Retrying with upper case share name\n");
2104                                 already_uppercased = 1;
2105                                 goto mount_retry;
2106                         }
2107                 }
2108                 fprintf(stderr, "mount error(%d): %s\n", errno,
2109                         strerror(errno));
2110                 fprintf(stderr,
2111                         "Refer to the %s(8) manual page (e.g. man "
2112                         "%s)\n", thisprogram, thisprogram);
2113                 rc = EX_FAIL;
2114                 goto mount_exit;
2115         }
2116
2117 do_mtab:
2118         if (!parsed_info->nomtab && !mtab_unusable()) {
2119                 if (parsed_info->flags & MS_REMOUNT) {
2120                         rc = del_mtab(mountpoint);
2121                         if (rc)
2122                                 goto mount_exit;
2123                 }
2124
2125                 rc = add_mtab(orig_dev, mountpoint, parsed_info->flags, cifs_fstype);
2126         }
2127
2128 mount_exit:
2129         if (parsed_info) {
2130                 memset(parsed_info->password, 0, sizeof(parsed_info->password));
2131                 munmap(parsed_info, sizeof(*parsed_info));
2132         }
2133         SAFE_FREE(options);
2134         SAFE_FREE(orgoptions);
2135         return rc;
2136 }