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