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