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