mount.cifs: check_newline returns EX_USAGE on error, not -1
[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 = 0;
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                         errno = 0;
1035                         uid = strtoul(value, &ep, 10);
1036                         if (errno == 0)
1037                                 goto nocopy;
1038
1039                         pw = getpwnam(value);
1040                         if (pw == NULL) {
1041                                 fprintf(stderr, "bad user name \"%s\"\n", value);
1042                                 return EX_USAGE;
1043                         }
1044
1045                         uid = pw->pw_uid;
1046                         goto nocopy;
1047
1048                 case OPT_CRUID:
1049                         if (!value || !*value)
1050                                 goto nocopy;
1051
1052                         got_cruid = 1;
1053                         errno = 0;
1054                         cruid = strtoul(value, &ep, 10);
1055                         if (errno == 0)
1056                                 goto nocopy;
1057
1058                         pw = getpwnam(value);
1059                         if (pw == NULL) {
1060                                 fprintf(stderr, "bad user name \"%s\"\n", value);
1061                                 return EX_USAGE;
1062                         }
1063                         cruid = pw->pw_uid;
1064                         goto nocopy;
1065
1066                 case OPT_GID:
1067                         if (!value || !*value)
1068                                 goto nocopy;
1069
1070                         got_gid = 1;
1071                         errno = 0;
1072                         gid = strtoul(value, &ep, 10);
1073                         if (errno == 0)
1074                                 goto nocopy;
1075
1076                         gr = getgrnam(value);
1077                         if (gr == NULL) {
1078                                 fprintf(stderr, "bad group name \"%s\"\n", value);
1079                                 return EX_USAGE;
1080                         }
1081
1082                         gid = gr->gr_gid;
1083                         goto nocopy;
1084
1085                 /* fmask fall through to file_mode */
1086                 case OPT_FMASK:
1087                         fprintf(stderr,
1088                                 "WARNING: CIFS mount option 'fmask' is\
1089                                  deprecated. Use 'file_mode' instead.\n");
1090                         data = "file_mode";     /* BB fix this */
1091                 case OPT_FILE_MODE:
1092                         if (!value || !*value) {
1093                                 fprintf(stderr,
1094                                         "Option '%s' requires a numerical argument\n",
1095                                         data);
1096                                 return EX_USAGE;
1097                         }
1098
1099                         if (value[0] != '0')
1100                                 fprintf(stderr,
1101                                         "WARNING: '%s' not expressed in octal.\n",
1102                                         data);
1103                         break;
1104
1105                 /* dmask falls through to dir_mode */
1106                 case OPT_DMASK:
1107                         fprintf(stderr,
1108                                 "WARNING: CIFS mount option 'dmask' is\
1109                                  deprecated. Use 'dir_mode' instead.\n");
1110                         data = "dir_mode";
1111                 case OPT_DIR_MODE:
1112                         if (!value || !*value) {
1113                                 fprintf(stderr,
1114                                         "Option '%s' requires a numerical argument\n",
1115                                         data);
1116                                 return EX_USAGE;
1117                         }
1118
1119                         if (value[0] != '0')
1120                                 fprintf(stderr,
1121                                         "WARNING: '%s' not expressed in octal.\n",
1122                                         data);
1123                         break;
1124
1125                 /* the following mount options should be
1126                    stripped out from what is passed into the kernel
1127                    since these options are best passed as the
1128                    mount flags rather than redundantly to the kernel
1129                    and could generate spurious warnings depending on the
1130                    level of the corresponding cifs vfs kernel code */
1131                 case OPT_NO_SUID:
1132                         *filesys_flags |= MS_NOSUID;
1133                         break;
1134                 case OPT_SUID:
1135                         *filesys_flags &= ~MS_NOSUID;
1136                         break;
1137                 case OPT_NO_DEV:
1138                         *filesys_flags |= MS_NODEV;
1139                         break;
1140                 /* nolock || nobrl */
1141                 case OPT_NO_LOCK:
1142                         *filesys_flags &= ~MS_MANDLOCK;
1143                         break;
1144                 case OPT_MAND:
1145                         *filesys_flags |= MS_MANDLOCK;
1146                         goto nocopy;
1147                 case OPT_NOMAND:
1148                         *filesys_flags &= ~MS_MANDLOCK;
1149                         goto nocopy;
1150                 case OPT_DEV:
1151                         *filesys_flags &= ~MS_NODEV;
1152                         break;
1153                 case OPT_NO_EXEC:
1154                         *filesys_flags |= MS_NOEXEC;
1155                         break;
1156                 case OPT_EXEC:
1157                         *filesys_flags &= ~MS_NOEXEC;
1158                         break;
1159                 case OPT_GUEST:
1160                         parsed_info->got_user = 1;
1161                         parsed_info->got_password = 1;
1162                         break;
1163                 case OPT_RO:
1164                         *filesys_flags |= MS_RDONLY;
1165                         goto nocopy;
1166                 case OPT_RW:
1167                         *filesys_flags &= ~MS_RDONLY;
1168                         goto nocopy;
1169                 case OPT_REMOUNT:
1170                         *filesys_flags |= MS_REMOUNT;
1171                         break;
1172                 case OPT_IGNORE:
1173                         goto nocopy;
1174                 }
1175
1176                 /* check size before copying option to buffer */
1177                 word_len = strlen(data);
1178                 if (value)
1179                         word_len += 1 + strlen(value);
1180
1181                 /* need 2 extra bytes for comma and null byte */
1182                 if (out_len + word_len + 2 > MAX_OPTIONS_LEN) {
1183                         fprintf(stderr, "Options string too long\n");
1184                         return EX_USAGE;
1185                 }
1186
1187                 /* put back equals sign, if any */
1188                 if (equals)
1189                         *equals = '=';
1190
1191                 /* go ahead and copy */
1192                 if (out_len)
1193                         strlcat(out, ",", MAX_OPTIONS_LEN);
1194
1195                 strlcat(out, data, MAX_OPTIONS_LEN);
1196                 out_len = strlen(out);
1197 nocopy:
1198                 data = next_keyword;
1199         }
1200
1201         /* special-case the uid and gid */
1202         if (got_uid) {
1203                 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", uid);
1204
1205                 /* comma + "uid=" + terminating NULL == 6 */
1206                 if (out_len + word_len + 6 > MAX_OPTIONS_LEN) {
1207                         fprintf(stderr, "Options string too long\n");
1208                         return EX_USAGE;
1209                 }
1210
1211                 if (out_len) {
1212                         strlcat(out, ",", MAX_OPTIONS_LEN);
1213                         out_len++;
1214                 }
1215                 snprintf(out + out_len, word_len + 5, "uid=%s", txtbuf);
1216                 out_len = strlen(out);
1217         }
1218         if (got_cruid) {
1219                 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", cruid);
1220
1221                 /* comma + "cruid=" + terminating NULL == 6 */
1222                 if (out_len + word_len + 8 > MAX_OPTIONS_LEN) {
1223                         fprintf(stderr, "Options string too long\n");
1224                         return EX_USAGE;
1225                 }
1226
1227                 if (out_len) {
1228                         strlcat(out, ",", MAX_OPTIONS_LEN);
1229                         out_len++;
1230                 }
1231                 snprintf(out + out_len, word_len + 7, "cruid=%s", txtbuf);
1232                 out_len = strlen(out);
1233         }
1234         if (got_gid) {
1235                 word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", gid);
1236
1237                 /* comma + "gid=" + terminating NULL == 6 */
1238                 if (out_len + word_len + 6 > MAX_OPTIONS_LEN) {
1239                         fprintf(stderr, "Options string too long\n");
1240                         return EX_USAGE;
1241                 }
1242
1243                 if (out_len) {
1244                         strlcat(out, ",", MAX_OPTIONS_LEN);
1245                         out_len++;
1246                 }
1247                 snprintf(out + out_len, word_len + 5, "gid=%s", txtbuf);
1248         }
1249         return 0;
1250 }
1251
1252 static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info)
1253 {
1254         int length = strnlen(unc_name, MAX_UNC_LEN);
1255         const char *host, *share, *prepath;
1256         size_t hostlen, sharelen, prepathlen;
1257
1258         if (length > (MAX_UNC_LEN - 1)) {
1259                 fprintf(stderr, "mount error: UNC name too long\n");
1260                 return EX_USAGE;
1261         }
1262
1263         if (length < 3) {
1264                 fprintf(stderr, "mount error: UNC name too short\n");
1265                 return EX_USAGE;
1266         }
1267
1268         if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1269             (strncasecmp("smb://", unc_name, 6) == 0)) {
1270                 fprintf(stderr,
1271                         "Mounting cifs URL not implemented yet. Attempt to mount %s\n",
1272                         unc_name);
1273                 return EX_USAGE;
1274         }
1275
1276         /* Set up "host" and "share" pointers based on UNC format. */
1277         if (strncmp(unc_name, "//", 2) && strncmp(unc_name, "\\\\", 2)) {
1278                 /*
1279                  * check for nfs syntax (server:/share/prepath)
1280                  *
1281                  * FIXME: IPv6 addresses?
1282                  */
1283                 host = unc_name;
1284                 share = strchr(host, ':');
1285                 if (!share) {
1286                         fprintf(stderr, "mount.cifs: bad UNC (%s)\n", unc_name);
1287                         return EX_USAGE;
1288                 }
1289                 hostlen = share - host;
1290                 share++;
1291                 if (*share == '/')
1292                         ++share;
1293         } else {
1294                 host = unc_name + 2;
1295                 hostlen = strcspn(host, "/\\");
1296                 if (!hostlen) {
1297                         fprintf(stderr, "mount.cifs: bad UNC (%s)\n", unc_name);
1298                         return EX_USAGE;
1299                 }
1300                 share = host + hostlen + 1;
1301         }
1302
1303         if (hostlen + 1 > sizeof(parsed_info->host)) {
1304                 fprintf(stderr, "mount.cifs: host portion of UNC too long\n");
1305                 return EX_USAGE;
1306         }
1307
1308         sharelen = strcspn(share, "/\\");
1309         if (sharelen + 1 > sizeof(parsed_info->share)) {
1310                 fprintf(stderr, "mount.cifs: share portion of UNC too long\n");
1311                 return EX_USAGE;
1312         }
1313
1314         prepath = share + sharelen;
1315         if (*prepath != '\0')
1316                 prepath++;
1317
1318         prepathlen = strlen(prepath);
1319
1320         if (prepathlen + 1 > sizeof(parsed_info->prefix)) {
1321                 fprintf(stderr, "mount.cifs: UNC prefixpath too long\n");
1322                 return EX_USAGE;
1323         }
1324
1325         /* copy pieces into their resepective buffers */
1326         memcpy(parsed_info->host, host, hostlen);
1327         memcpy(parsed_info->share, share, sharelen);
1328         memcpy(parsed_info->prefix, prepath, prepathlen);
1329
1330         return 0;
1331 }
1332
1333 static int get_pw_from_env(struct parsed_mount_info *parsed_info)
1334 {
1335         int rc = 0;
1336
1337         if (getenv("PASSWD"))
1338                 rc = set_password(parsed_info, getenv("PASSWD"));
1339         else if (getenv("PASSWD_FD"))
1340                 rc = get_password_from_file(atoi(getenv("PASSWD_FD")), NULL,
1341                                             parsed_info);
1342         else if (getenv("PASSWD_FILE"))
1343                 rc = get_password_from_file(0, getenv("PASSWD_FILE"),
1344                                             parsed_info);
1345
1346         return rc;
1347 }
1348
1349 static struct option longopts[] = {
1350         {"all", 0, NULL, 'a'},
1351         {"help", 0, NULL, 'h'},
1352         {"move", 0, NULL, 'm'},
1353         {"bind", 0, NULL, 'b'},
1354         {"read-only", 0, NULL, 'r'},
1355         {"ro", 0, NULL, 'r'},
1356         {"verbose", 0, NULL, 'v'},
1357         {"version", 0, NULL, 'V'},
1358         {"read-write", 0, NULL, 'w'},
1359         {"rw", 0, NULL, 'w'},
1360         {"options", 1, NULL, 'o'},
1361         {"type", 1, NULL, 't'},
1362         {"uid", 1, NULL, '1'},
1363         {"gid", 1, NULL, '2'},
1364         {"user", 1, NULL, 'u'},
1365         {"username", 1, NULL, 'u'},
1366         {"dom", 1, NULL, 'd'},
1367         {"domain", 1, NULL, 'd'},
1368         {"password", 1, NULL, 'p'},
1369         {"pass", 1, NULL, 'p'},
1370         {"credentials", 1, NULL, 'c'},
1371         {"port", 1, NULL, 'P'},
1372         {NULL, 0, NULL, 0}
1373 };
1374
1375 /* convert a string to uppercase. return false if the string
1376  * wasn't ASCII. Return success on a NULL ptr */
1377 static int uppercase_string(char *string)
1378 {
1379         if (!string)
1380                 return 1;
1381
1382         while (*string) {
1383                 /* check for unicode */
1384                 if ((unsigned char)string[0] & 0x80)
1385                         return 0;
1386                 *string = toupper((unsigned char)*string);
1387                 string++;
1388         }
1389
1390         return 1;
1391 }
1392
1393 static void print_cifs_mount_version(void)
1394 {
1395         printf("mount.cifs version: %s\n", VERSION);
1396 }
1397
1398 /*
1399  * This function borrowed from fuse-utils...
1400  *
1401  * glibc's addmntent (at least as of 2.10 or so) doesn't properly encode
1402  * newlines embedded within the text fields. To make sure no one corrupts
1403  * the mtab, fail the mount if there are embedded newlines.
1404  */
1405 static int check_newline(const char *progname, const char *name)
1406 {
1407         const char *s;
1408         for (s = "\n"; *s; s++) {
1409                 if (strchr(name, *s)) {
1410                         fprintf(stderr,
1411                                 "%s: illegal character 0x%02x in mount entry\n",
1412                                 progname, *s);
1413                         return EX_USAGE;
1414                 }
1415         }
1416         return 0;
1417 }
1418
1419 static int check_mtab(const char *progname, const char *devname,
1420                       const char *dir)
1421 {
1422         if (check_newline(progname, devname) || check_newline(progname, dir))
1423                 return EX_USAGE;
1424         return 0;
1425 }
1426
1427 static int
1428 add_mtab(char *devname, char *mountpoint, unsigned long flags, const char *fstype)
1429 {
1430         int rc = 0, tmprc, fd;
1431         uid_t uid;
1432         char *mount_user = NULL;
1433         struct mntent mountent;
1434         struct stat statbuf;
1435         FILE *pmntfile;
1436         sigset_t mask, oldmask;
1437
1438         uid = getuid();
1439         if (uid != 0)
1440                 mount_user = getusername(uid);
1441
1442         /*
1443          * Set the real uid to the effective uid. This prevents unprivileged
1444          * users from sending signals to this process, though ^c on controlling
1445          * terminal should still work.
1446          */
1447         rc = setreuid(geteuid(), -1);
1448         if (rc != 0) {
1449                 fprintf(stderr, "Unable to set real uid to effective uid: %s\n",
1450                                 strerror(errno));
1451                 return EX_FILEIO;
1452         }
1453
1454         rc = sigfillset(&mask);
1455         if (rc) {
1456                 fprintf(stderr, "Unable to set filled signal mask\n");
1457                 return EX_FILEIO;
1458         }
1459
1460         rc = sigprocmask(SIG_SETMASK, &mask, &oldmask);
1461         if (rc) {
1462                 fprintf(stderr, "Unable to make process ignore signals\n");
1463                 return EX_FILEIO;
1464         }
1465
1466         rc = toggle_dac_capability(1, 1);
1467         if (rc)
1468                 return EX_FILEIO;
1469
1470         atexit(unlock_mtab);
1471         rc = lock_mtab();
1472         if (rc) {
1473                 fprintf(stderr, "cannot lock mtab");
1474                 rc = EX_FILEIO;
1475                 goto add_mtab_exit;
1476         }
1477
1478         pmntfile = setmntent(MOUNTED, "a+");
1479         if (!pmntfile) {
1480                 fprintf(stderr, "could not update mount table\n");
1481                 unlock_mtab();
1482                 rc = EX_FILEIO;
1483                 goto add_mtab_exit;
1484         }
1485
1486         fd = fileno(pmntfile);
1487         if (fd < 0) {
1488                 fprintf(stderr, "mntent does not appear to be valid\n");
1489                 unlock_mtab();
1490                 rc = EX_FILEIO;
1491                 goto add_mtab_exit;
1492         }
1493
1494         rc = fstat(fd, &statbuf);
1495         if (rc != 0) {
1496                 fprintf(stderr, "unable to fstat open mtab\n");
1497                 endmntent(pmntfile);
1498                 unlock_mtab();
1499                 rc = EX_FILEIO;
1500                 goto add_mtab_exit;
1501         }
1502
1503         mountent.mnt_fsname = devname;
1504         mountent.mnt_dir = mountpoint;
1505         mountent.mnt_type = (char *)(void *)fstype;
1506         mountent.mnt_opts = (char *)calloc(MTAB_OPTIONS_LEN, 1);
1507         if (mountent.mnt_opts) {
1508                 if (flags & MS_RDONLY)
1509                         strlcat(mountent.mnt_opts, "ro", MTAB_OPTIONS_LEN);
1510                 else
1511                         strlcat(mountent.mnt_opts, "rw", MTAB_OPTIONS_LEN);
1512
1513                 if (flags & MS_MANDLOCK)
1514                         strlcat(mountent.mnt_opts, ",mand", MTAB_OPTIONS_LEN);
1515                 if (flags & MS_NOEXEC)
1516                         strlcat(mountent.mnt_opts, ",noexec", MTAB_OPTIONS_LEN);
1517                 if (flags & MS_NOSUID)
1518                         strlcat(mountent.mnt_opts, ",nosuid", MTAB_OPTIONS_LEN);
1519                 if (flags & MS_NODEV)
1520                         strlcat(mountent.mnt_opts, ",nodev", MTAB_OPTIONS_LEN);
1521                 if (flags & MS_SYNCHRONOUS)
1522                         strlcat(mountent.mnt_opts, ",sync", MTAB_OPTIONS_LEN);
1523                 if (mount_user) {
1524                         strlcat(mountent.mnt_opts, ",user=", MTAB_OPTIONS_LEN);
1525                         strlcat(mountent.mnt_opts, mount_user,
1526                                 MTAB_OPTIONS_LEN);
1527                 }
1528         }
1529         mountent.mnt_freq = 0;
1530         mountent.mnt_passno = 0;
1531         rc = addmntent(pmntfile, &mountent);
1532         if (rc) {
1533                 fprintf(stderr, "unable to add mount entry to mtab\n");
1534                 ftruncate(fd, statbuf.st_size);
1535                 rc = EX_FILEIO;
1536         }
1537         tmprc = my_endmntent(pmntfile, statbuf.st_size);
1538         if (tmprc) {
1539                 fprintf(stderr, "error %d detected on close of mtab\n", tmprc);
1540                 rc = EX_FILEIO;
1541         }
1542         unlock_mtab();
1543         SAFE_FREE(mountent.mnt_opts);
1544 add_mtab_exit:
1545         toggle_dac_capability(1, 0);
1546         sigprocmask(SIG_SETMASK, &oldmask, NULL);
1547
1548         return rc;
1549 }
1550
1551 /* have the child drop root privileges */
1552 static int
1553 drop_child_privs(void)
1554 {
1555         int rc;
1556         uid_t uid = getuid();
1557         gid_t gid = getgid();
1558
1559         if (gid) {
1560                 rc = setgid(gid);
1561                 if (rc) {
1562                         fprintf(stderr, "Unable set group identity: %s\n",
1563                                         strerror(errno));
1564                         return EX_SYSERR;
1565                 }
1566         }
1567         if (uid) {
1568                 rc = setuid(uid);
1569                 if (rc) {
1570                         fprintf(stderr, "Unable set user identity: %s\n",
1571                                         strerror(errno));
1572                         return EX_SYSERR;
1573                 }
1574         }
1575
1576         return 0;
1577 }
1578
1579 static int
1580 assemble_mountinfo(struct parsed_mount_info *parsed_info,
1581                    const char *thisprogram, const char *mountpoint,
1582                    const char *orig_dev, char *orgoptions)
1583 {
1584         int rc;
1585
1586         rc = drop_capabilities(0);
1587         if (rc)
1588                 goto assemble_exit;
1589
1590         rc = drop_child_privs();
1591         if (rc)
1592                 goto assemble_exit;
1593
1594         if (getuid()) {
1595                 rc = check_fstab(thisprogram, mountpoint, orig_dev,
1596                                  &orgoptions);
1597                 if (rc)
1598                         goto assemble_exit;
1599
1600                 /* enable any default user mount flags */
1601                 parsed_info->flags |= CIFS_SETUID_FLAGS;
1602         }
1603
1604         rc = get_pw_from_env(parsed_info);
1605         if (rc)
1606                 goto assemble_exit;
1607
1608         if (orgoptions) {
1609                 rc = parse_options(orgoptions, parsed_info);
1610                 if (rc)
1611                         goto assemble_exit;
1612         }
1613
1614         if (getuid()) {
1615                 if (!(parsed_info->flags & (MS_USERS | MS_USER))) {
1616                         fprintf(stderr, "%s: permission denied\n", thisprogram);
1617                         rc = EX_USAGE;
1618                         goto assemble_exit;
1619                 }
1620         }
1621
1622         parsed_info->flags &= ~(MS_USERS | MS_USER);
1623
1624         rc = parse_unc(orig_dev, parsed_info);
1625         if (rc)
1626                 goto assemble_exit;
1627
1628         if (parsed_info->addrlist[0] == '\0')
1629                 rc = resolve_host(parsed_info->host, parsed_info->addrlist);
1630
1631         switch (rc) {
1632         case EX_USAGE:
1633                 fprintf(stderr, "mount error: could not resolve address for "
1634                         "%s: %s\n", parsed_info->host,
1635                         rc == EAI_SYSTEM ? strerror(errno) : gai_strerror(rc));
1636                 goto assemble_exit;
1637
1638         case EX_SYSERR:
1639                 fprintf(stderr, "mount error: problem parsing address "
1640                         "list: %s\n", strerror(errno));
1641                 goto assemble_exit;
1642         }
1643
1644         if (!parsed_info->got_user) {
1645                 /*
1646                  * Note that the password will not be retrieved from the
1647                  * USER env variable (ie user%password form) as there is
1648                  * already a PASSWD environment varaible
1649                  */
1650                 if (getenv("USER"))
1651                         strlcpy(parsed_info->username, getenv("USER"),
1652                                 sizeof(parsed_info->username));
1653                 else
1654                         strlcpy(parsed_info->username, getusername(getuid()),
1655                                 sizeof(parsed_info->username));
1656                 parsed_info->got_user = 1;
1657         }
1658
1659         if (!parsed_info->got_password) {
1660                 /* getpass is obsolete, but there's apparently nothing that replaces it */
1661                 char *tmp_pass = getpass("Password: ");
1662                 if (!tmp_pass) {
1663                         fprintf(stderr, "Error reading password, exiting\n");
1664                         rc = EX_SYSERR;
1665                         goto assemble_exit;
1666                 }
1667                 rc = set_password(parsed_info, tmp_pass);
1668                 if (rc)
1669                         goto assemble_exit;
1670         }
1671
1672         /* copy in ver= string. It's not really needed, but what the hell */
1673         strlcat(parsed_info->options, ",ver=", sizeof(parsed_info->options));
1674         strlcat(parsed_info->options, OPTIONS_VERSION, sizeof(parsed_info->options));
1675
1676         /* copy in user= string */
1677         if (parsed_info->got_user) {
1678                 strlcat(parsed_info->options, ",user=",
1679                         sizeof(parsed_info->options));
1680                 strlcat(parsed_info->options, parsed_info->username,
1681                         sizeof(parsed_info->options));
1682         }
1683
1684         if (*parsed_info->domain) {
1685                 strlcat(parsed_info->options, ",domain=",
1686                         sizeof(parsed_info->options));
1687                 strlcat(parsed_info->options, parsed_info->domain,
1688                         sizeof(parsed_info->options));
1689         }
1690
1691 assemble_exit:
1692         return rc;
1693 }
1694
1695 int main(int argc, char **argv)
1696 {
1697         int c;
1698         char *orgoptions = NULL;
1699         char *mountpoint = NULL;
1700         char *options = NULL;
1701         char *orig_dev = NULL;
1702         char *currentaddress, *nextaddress;
1703         int rc = 0;
1704         int already_uppercased = 0;
1705         size_t options_size = MAX_OPTIONS_LEN;
1706         struct parsed_mount_info *parsed_info = NULL;
1707         pid_t pid;
1708         const char *fstype;
1709
1710         rc = check_setuid();
1711         if (rc)
1712                 return rc;
1713
1714         rc = drop_capabilities(1);
1715         if (rc)
1716                 return EX_SYSERR;
1717
1718         /* setlocale(LC_ALL, "");
1719            bindtextdomain(PACKAGE, LOCALEDIR);
1720            textdomain(PACKAGE); */
1721
1722         if (!argc || !argv) {
1723                 rc = mount_usage(stderr);
1724                 goto mount_exit;
1725         }
1726
1727         thisprogram = basename(argv[0]);
1728         if (thisprogram == NULL)
1729                 thisprogram = "mount.cifs";
1730
1731         /* allocate parsed_info as shared anonymous memory range */
1732         parsed_info = mmap(0, sizeof(*parsed_info), PROT_READ | PROT_WRITE,
1733                            MAP_ANONYMOUS | MAP_SHARED, -1, 0);
1734         if (parsed_info == (struct parsed_mount_info *) -1) {
1735                 parsed_info = NULL;
1736                 fprintf(stderr, "Unable to allocate memory: %s\n",
1737                                 strerror(errno));
1738                 return EX_SYSERR;
1739         }
1740
1741         /* add sharename in opts string as unc= parm */
1742         while ((c = getopt_long(argc, argv, "?fhno:rvVw",
1743                                 longopts, NULL)) != -1) {
1744                 switch (c) {
1745                 case '?':
1746                 case 'h':       /* help */
1747                         rc = mount_usage(stdout);
1748                         goto mount_exit;
1749                 case 'n':
1750                         ++parsed_info->nomtab;
1751                         break;
1752                 case 'o':
1753                         orgoptions = strndup(optarg, MAX_OPTIONS_LEN);
1754                         if (!orgoptions) {
1755                                 rc = EX_SYSERR;
1756                                 goto mount_exit;
1757                         }
1758                         break;
1759                 case 'r':       /* mount readonly */
1760                         parsed_info->flags |= MS_RDONLY;
1761                         break;
1762                 case 'v':
1763                         ++parsed_info->verboseflag;
1764                         break;
1765                 case 'V':
1766                         print_cifs_mount_version();
1767                         exit(0);
1768                 case 'w':
1769                         parsed_info->flags &= ~MS_RDONLY;
1770                         break;
1771                 case 'f':
1772                         ++parsed_info->fakemnt;
1773                         break;
1774                 default:
1775                         fprintf(stderr, "unknown command-line option: %c\n", c);
1776                         rc = mount_usage(stderr);
1777                         goto mount_exit;
1778                 }
1779         }
1780
1781         if (argc < 3 || argv[optind] == NULL || argv[optind + 1] == NULL) {
1782                 rc = mount_usage(stderr);
1783                 goto mount_exit;
1784         }
1785
1786         orig_dev = argv[optind];
1787         mountpoint = argv[optind + 1];
1788
1789         /* chdir into mountpoint as soon as possible */
1790         rc = toggle_dac_capability(0, 1);
1791         if (rc)
1792                 return rc;
1793         rc = chdir(mountpoint);
1794         if (rc) {
1795                 fprintf(stderr, "Couldn't chdir to %s: %s\n", mountpoint,
1796                         strerror(errno));
1797                 rc = EX_USAGE;
1798                 goto mount_exit;
1799         }
1800
1801         mountpoint = realpath(".", NULL);
1802         if (!mountpoint) {
1803                 fprintf(stderr, "Unable to resolve %s to canonical path: %s\n",
1804                         mountpoint, strerror(errno));
1805                 rc = EX_SYSERR;
1806                 goto mount_exit;
1807         }
1808         rc = toggle_dac_capability(0, 0);
1809         if (rc)
1810                 return rc;
1811
1812         /*
1813          * mount.cifs does privilege separation. Most of the code to handle
1814          * assembling the mount info is done in a child process that drops
1815          * privileges. The info is assembled in parsed_info which is a
1816          * shared, mmaped memory segment. The parent waits for the child to
1817          * exit and checks the return code. If it's anything but "0", then
1818          * the process exits without attempting anything further.
1819          */
1820         pid = fork();
1821         if (pid == -1) {
1822                 fprintf(stderr, "Unable to fork: %s\n", strerror(errno));
1823                 rc = EX_SYSERR;
1824                 goto mount_exit;
1825         } else if (!pid) {
1826                 /* child */
1827                 rc = assemble_mountinfo(parsed_info, thisprogram, mountpoint,
1828                                         orig_dev, orgoptions);
1829                 return rc;
1830         } else {
1831                 /* parent */
1832                 pid = wait(&rc);
1833                 if (!WIFEXITED(rc)) {
1834                         fprintf(stderr, "Child process terminated abnormally.\n");
1835                         rc = EX_SYSERR;
1836                         goto mount_exit;
1837                 }
1838                 rc = WEXITSTATUS(rc);
1839                 if (rc)
1840                         goto mount_exit;
1841         }
1842
1843         options = calloc(options_size, 1);
1844         if (!options) {
1845                 fprintf(stderr, "Unable to allocate memory.\n");
1846                 rc = EX_SYSERR;
1847                 goto mount_exit;
1848         }
1849
1850         currentaddress = parsed_info->addrlist;
1851         nextaddress = strchr(currentaddress, ',');
1852         if (nextaddress)
1853                 *nextaddress++ = '\0';
1854
1855 mount_retry:
1856         if (!currentaddress) {
1857                 fprintf(stderr, "Unable to find suitable address.\n");
1858                 rc = EX_SYSERR;
1859                 goto mount_exit;
1860         }
1861         strlcpy(options, "ip=", options_size);
1862         strlcat(options, currentaddress, options_size);
1863
1864         strlcat(options, ",unc=\\\\", options_size);
1865         strlcat(options, parsed_info->host, options_size);
1866         strlcat(options, "\\", options_size);
1867         strlcat(options, parsed_info->share, options_size);
1868
1869         if (*parsed_info->options) {
1870                 strlcat(options, ",", options_size);
1871                 strlcat(options, parsed_info->options, options_size);
1872         }
1873
1874         if (*parsed_info->prefix) {
1875                 strlcat(options, ",prefixpath=", options_size);
1876                 strlcat(options, parsed_info->prefix, options_size);
1877         }
1878
1879         if (parsed_info->verboseflag)
1880                 fprintf(stderr, "%s kernel mount options: %s",
1881                         thisprogram, options);
1882
1883         if (parsed_info->got_password) {
1884                 /*
1885                  * Commas have to be doubled, or else they will
1886                  * look like the parameter separator
1887                  */
1888                 strlcat(options, ",pass=", options_size);
1889                 strlcat(options, parsed_info->password, options_size);
1890                 if (parsed_info->verboseflag)
1891                         fprintf(stderr, ",pass=********");
1892         }
1893
1894         if (parsed_info->verboseflag)
1895                 fprintf(stderr, "\n");
1896
1897         rc = check_mtab(thisprogram, orig_dev, mountpoint);
1898         if (rc)
1899                 goto mount_exit;
1900
1901         if (strcmp(thisprogram, "mount.smb2") == 0)
1902                 fstype = smb2_fstype;
1903         else
1904                 fstype = cifs_fstype;
1905
1906         if (!parsed_info->fakemnt) {
1907                 toggle_dac_capability(0, 1);
1908                 rc = mount(orig_dev, ".", fstype, parsed_info->flags, options);
1909                 toggle_dac_capability(0, 0);
1910                 if (rc == 0)
1911                         goto do_mtab;
1912
1913                 switch (errno) {
1914                 case ECONNREFUSED:
1915                 case EHOSTUNREACH:
1916                         currentaddress = nextaddress;
1917                         if (currentaddress) {
1918                                 nextaddress = strchr(currentaddress, ',');
1919                                 if (nextaddress)
1920                                         *nextaddress++ = '\0';
1921                         }
1922                         goto mount_retry;
1923                 case ENODEV:
1924                         fprintf(stderr,
1925                                 "mount error: %s filesystem not supported by the system\n", fstype);
1926                         break;
1927                 case ENXIO:
1928                         if (!already_uppercased &&
1929                             uppercase_string(parsed_info->host) &&
1930                             uppercase_string(parsed_info->share) &&
1931                             uppercase_string(parsed_info->prefix)) {
1932                                 fprintf(stderr,
1933                                         "Retrying with upper case share name\n");
1934                                 already_uppercased = 1;
1935                                 goto mount_retry;
1936                         }
1937                 }
1938                 fprintf(stderr, "mount error(%d): %s\n", errno,
1939                         strerror(errno));
1940                 fprintf(stderr,
1941                         "Refer to the %s(8) manual page (e.g. man "
1942                         "%s)\n", thisprogram, thisprogram);
1943                 rc = EX_FAIL;
1944                 goto mount_exit;
1945         }
1946
1947 do_mtab:
1948         if (!parsed_info->nomtab && !mtab_unusable())
1949                 rc = add_mtab(orig_dev, mountpoint, parsed_info->flags, fstype);
1950
1951 mount_exit:
1952         if (parsed_info) {
1953                 memset(parsed_info->password, 0, sizeof(parsed_info->password));
1954                 munmap(parsed_info, sizeof(*parsed_info));
1955         }
1956         SAFE_FREE(options);
1957         SAFE_FREE(orgoptions);
1958         return rc;
1959 }