source: vendor/current/client/mount.cifs.c@ 597

Last change on this file since 597 was 478, checked in by Silvan Scherrer, 15 years ago

Samba 3.5: vendor update to 3.5.4

File size: 48.0 KB
Line 
1/*
2 Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3 Copyright (C) 2003,2008 Steve French (sfrench@us.ibm.com)
4 Copyright (C) 2008 Jeremy Allison (jra@samba.org)
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18
19#ifndef _GNU_SOURCE
20#define _GNU_SOURCE
21#endif
22
23#include <stdlib.h>
24#include <stdio.h>
25#include <unistd.h>
26#include <pwd.h>
27#include <grp.h>
28#include <ctype.h>
29#include <sys/types.h>
30#include <sys/mount.h>
31#include <sys/stat.h>
32#include <sys/utsname.h>
33#include <sys/socket.h>
34#include <arpa/inet.h>
35#include <getopt.h>
36#include <errno.h>
37#include <netdb.h>
38#include <string.h>
39#include <mntent.h>
40#include <fcntl.h>
41#include <limits.h>
42#include <fstab.h>
43#include "mount.h"
44
45#define MOUNT_CIFS_VERSION_MAJOR "1"
46#define MOUNT_CIFS_VERSION_MINOR "14"
47
48#ifndef MOUNT_CIFS_VENDOR_SUFFIX
49 #ifdef _SAMBA_BUILD_
50 #include "version.h"
51 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
52 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
53 #else
54 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
55 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
56 #else
57 #define MOUNT_CIFS_VENDOR_SUFFIX ""
58 #endif /* _SAMBA_BUILD_ */
59#endif /* MOUNT_CIFS_VENDOR_SUFFIX */
60
61#ifdef _SAMBA_BUILD_
62#include "include/config.h"
63#endif
64
65#ifndef MS_MOVE
66#define MS_MOVE 8192
67#endif
68
69#ifndef MS_BIND
70#define MS_BIND 4096
71#endif
72
73/* private flags - clear these before passing to kernel */
74#define MS_USERS 0x40000000
75#define MS_USER 0x80000000
76
77#define MAX_UNC_LEN 1024
78
79#define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
80
81#ifndef SAFE_FREE
82#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
83#endif
84
85#define MOUNT_PASSWD_SIZE 128
86#define DOMAIN_SIZE 64
87
88/* currently maximum length of IPv6 address string */
89#define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
90
91/*
92 * mount.cifs has been the subject of many "security" bugs that have arisen
93 * because of users and distributions installing it as a setuid root program.
94 * mount.cifs has not been audited for security. Thus, we strongly recommend
95 * that it not be installed setuid root. To make that abundantly clear,
96 * mount.cifs now check whether it's running setuid root and exit with an
97 * error if it is. If you wish to disable this check, then set the following
98 * #define to 1, but please realize that you do so at your own peril.
99 */
100#define CIFS_DISABLE_SETUID_CHECK 0
101
102/*
103 * By default, mount.cifs follows the conventions set forth by /bin/mount
104 * for user mounts. That is, it requires that the mount be listed in
105 * /etc/fstab with the "user" option when run as an unprivileged user and
106 * mount.cifs is setuid root.
107 *
108 * Older versions of mount.cifs however were "looser" in this regard. When
109 * made setuid root, a user could run mount.cifs directly and mount any share
110 * on a directory owned by that user.
111 *
112 * The legacy behavior is now disabled by default. To reenable it, set the
113 * following #define to true.
114 */
115#define CIFS_LEGACY_SETUID_CHECK 0
116
117/*
118 * When an unprivileged user runs a setuid mount.cifs, we set certain mount
119 * flags by default. These defaults can be changed here.
120 */
121#define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
122
123const char *thisprogram;
124int verboseflag = 0;
125int fakemnt = 0;
126static int got_password = 0;
127static int got_user = 0;
128static int got_domain = 0;
129static int got_ip = 0;
130static int got_unc = 0;
131static int got_uid = 0;
132static int got_gid = 0;
133static char * user_name = NULL;
134static char * mountpassword = NULL;
135char * domain_name = NULL;
136char * prefixpath = NULL;
137
138/* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
139 * don't link to libreplace so need them here. */
140
141/* like strncpy but does not 0 fill the buffer and always null
142 * terminates. bufsize is the size of the destination buffer */
143
144#ifndef HAVE_STRLCPY
145static size_t strlcpy(char *d, const char *s, size_t bufsize)
146{
147 size_t len = strlen(s);
148 size_t ret = len;
149 if (bufsize <= 0) return 0;
150 if (len >= bufsize) len = bufsize-1;
151 memcpy(d, s, len);
152 d[len] = 0;
153 return ret;
154}
155#endif
156
157/* like strncat but does not 0 fill the buffer and always null
158 * terminates. bufsize is the length of the buffer, which should
159 * be one more than the maximum resulting string length */
160
161#ifndef HAVE_STRLCAT
162static size_t strlcat(char *d, const char *s, size_t bufsize)
163{
164 size_t len1 = strlen(d);
165 size_t len2 = strlen(s);
166 size_t ret = len1 + len2;
167
168 if (len1+len2 >= bufsize) {
169 if (bufsize < (len1+1)) {
170 return ret;
171 }
172 len2 = bufsize - (len1+1);
173 }
174 if (len2 > 0) {
175 memcpy(d+len1, s, len2);
176 d[len1+len2] = 0;
177 }
178 return ret;
179}
180#endif
181
182/*
183 * If an unprivileged user is doing the mounting then we need to ensure
184 * that the entry is in /etc/fstab.
185 */
186static int
187check_mountpoint(const char *progname, char *mountpoint)
188{
189 int err;
190 struct stat statbuf;
191
192 /* does mountpoint exist and is it a directory? */
193 err = stat(".", &statbuf);
194 if (err) {
195 fprintf(stderr, "%s: failed to stat %s: %s\n", progname,
196 mountpoint, strerror(errno));
197 return EX_USAGE;
198 }
199
200 if (!S_ISDIR(statbuf.st_mode)) {
201 fprintf(stderr, "%s: %s is not a directory!", progname,
202 mountpoint);
203 return EX_USAGE;
204 }
205
206#if CIFS_LEGACY_SETUID_CHECK
207 /* do extra checks on mountpoint for legacy setuid behavior */
208 if (!getuid() || geteuid())
209 return 0;
210
211 if (statbuf.st_uid != getuid()) {
212 fprintf(stderr, "%s: %s is not owned by user\n", progname,
213 mountpoint);
214 return EX_USAGE;
215 }
216
217 if ((statbuf.st_mode & S_IRWXU) != S_IRWXU) {
218 fprintf(stderr, "%s: invalid permissions on %s\n", progname,
219 mountpoint);
220 return EX_USAGE;
221 }
222#endif /* CIFS_LEGACY_SETUID_CHECK */
223
224 return 0;
225}
226
227#if CIFS_DISABLE_SETUID_CHECK
228static int
229check_setuid(void)
230{
231 return 0;
232}
233#else /* CIFS_DISABLE_SETUID_CHECK */
234static int
235check_setuid(void)
236{
237 if (getuid() && !geteuid()) {
238 printf("This mount.cifs program has been built with the "
239 "ability to run as a setuid root program disabled.\n"
240 "mount.cifs has not been well audited for security "
241 "holes. Therefore the Samba team does not recommend "
242 "installing it as a setuid root program.\n");
243 return 1;
244 }
245
246 return 0;
247}
248#endif /* CIFS_DISABLE_SETUID_CHECK */
249
250#if CIFS_LEGACY_SETUID_CHECK
251static int
252check_fstab(const char *progname, char *mountpoint, char *devname,
253 char **options)
254{
255 return 0;
256}
257#else /* CIFS_LEGACY_SETUID_CHECK */
258static int
259check_fstab(const char *progname, char *mountpoint, char *devname,
260 char **options)
261{
262 FILE *fstab;
263 struct mntent *mnt;
264
265 /* make sure this mount is listed in /etc/fstab */
266 fstab = setmntent(_PATH_FSTAB, "r");
267 if (!fstab) {
268 fprintf(stderr, "Couldn't open %s for reading!\n",
269 _PATH_FSTAB);
270 return EX_FILEIO;
271 }
272
273 while((mnt = getmntent(fstab))) {
274 if (!strcmp(mountpoint, mnt->mnt_dir))
275 break;
276 }
277 endmntent(fstab);
278
279 if (mnt == NULL || strcmp(mnt->mnt_fsname, devname)) {
280 fprintf(stderr, "%s: permission denied: no match for "
281 "%s found in %s\n", progname, mountpoint,
282 _PATH_FSTAB);
283 return EX_USAGE;
284 }
285
286 /*
287 * 'mount' munges the options from fstab before passing them
288 * to us. It is non-trivial to test that we have the correct
289 * set of options. We don't want to trust what the user
290 * gave us, so just take whatever is in /etc/fstab.
291 */
292 free(*options);
293 *options = strdup(mnt->mnt_opts);
294 return 0;
295}
296#endif /* CIFS_LEGACY_SETUID_CHECK */
297
298/* BB finish BB
299
300 cifs_umount
301 open nofollow - avoid symlink exposure?
302 get owner of dir see if matches self or if root
303 call system(umount argv) etc.
304
305BB end finish BB */
306
307static char * check_for_domain(char **);
308
309
310static void mount_cifs_usage(FILE *stream)
311{
312 fprintf(stream, "\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
313 fprintf(stream, "\nMount the remote target, specified as a UNC name,");
314 fprintf(stream, " to a local directory.\n\nOptions:\n");
315 fprintf(stream, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
316 fprintf(stream, "\nLess commonly used options:");
317 fprintf(stream, "\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
318 fprintf(stream, "\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
319 fprintf(stream, "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
320 fprintf(stream, "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
321 fprintf(stream, "\n\nOptions not needed for servers supporting CIFS Unix extensions");
322 fprintf(stream, "\n\t(e.g. unneeded for mounts to most Samba versions):");
323 fprintf(stream, "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
324 fprintf(stream, "\n\nRarely used options:");
325 fprintf(stream, "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
326 fprintf(stream, "\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
327 fprintf(stream, "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
328 fprintf(stream, "\n\nOptions are described in more detail in the manual page");
329 fprintf(stream, "\n\tman 8 mount.cifs\n");
330 fprintf(stream, "\nTo display the version number of the mount helper:");
331 fprintf(stream, "\n\t%s -V\n",thisprogram);
332
333 SAFE_FREE(mountpassword);
334
335 if (stream == stderr)
336 exit(EX_USAGE);
337 exit(0);
338}
339
340/* caller frees username if necessary */
341static char * getusername(void) {
342 char *username = NULL;
343 struct passwd *password = getpwuid(getuid());
344
345 if (password) {
346 username = password->pw_name;
347 }
348 return username;
349}
350
351static int open_cred_file(char * file_name)
352{
353 char * line_buf;
354 char * temp_val;
355 FILE * fs;
356 int i, length;
357
358 i = access(file_name, R_OK);
359 if (i)
360 return i;
361
362 fs = fopen(file_name,"r");
363 if(fs == NULL)
364 return errno;
365 line_buf = (char *)malloc(4096);
366 if(line_buf == NULL) {
367 fclose(fs);
368 return ENOMEM;
369 }
370
371 while(fgets(line_buf,4096,fs)) {
372 /* parse line from credential file */
373
374 /* eat leading white space */
375 for(i=0;i<4086;i++) {
376 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
377 break;
378 /* if whitespace - skip past it */
379 }
380 if (strncasecmp("username",line_buf+i,8) == 0) {
381 temp_val = strchr(line_buf + i,'=');
382 if(temp_val) {
383 /* go past equals sign */
384 temp_val++;
385 for(length = 0;length<4087;length++) {
386 if ((temp_val[length] == '\n')
387 || (temp_val[length] == '\0')) {
388 temp_val[length] = '\0';
389 break;
390 }
391 }
392 if(length > 4086) {
393 fprintf(stderr, "mount.cifs failed due to malformed username in credentials file\n");
394 memset(line_buf,0,4096);
395 exit(EX_USAGE);
396 } else {
397 got_user = 1;
398 user_name = (char *)calloc(1 + length,1);
399 /* BB adding free of user_name string before exit,
400 not really necessary but would be cleaner */
401 strlcpy(user_name,temp_val, length+1);
402 }
403 }
404 } else if (strncasecmp("password",line_buf+i,8) == 0) {
405 temp_val = strchr(line_buf+i,'=');
406 if(temp_val) {
407 /* go past equals sign */
408 temp_val++;
409 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
410 if ((temp_val[length] == '\n')
411 || (temp_val[length] == '\0')) {
412 temp_val[length] = '\0';
413 break;
414 }
415 }
416 if(length > MOUNT_PASSWD_SIZE) {
417 fprintf(stderr, "mount.cifs failed: password in credentials file too long\n");
418 memset(line_buf,0, 4096);
419 exit(EX_USAGE);
420 } else {
421 if(mountpassword == NULL) {
422 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
423 } else
424 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
425 if(mountpassword) {
426 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
427 got_password = 1;
428 }
429 }
430 }
431 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
432 temp_val = strchr(line_buf+i,'=');
433 if(temp_val) {
434 /* go past equals sign */
435 temp_val++;
436 if(verboseflag)
437 fprintf(stderr, "\nDomain %s\n",temp_val);
438 for(length = 0;length<DOMAIN_SIZE+1;length++) {
439 if ((temp_val[length] == '\n')
440 || (temp_val[length] == '\0')) {
441 temp_val[length] = '\0';
442 break;
443 }
444 }
445 if(length > DOMAIN_SIZE) {
446 fprintf(stderr, "mount.cifs failed: domain in credentials file too long\n");
447 exit(EX_USAGE);
448 } else {
449 if(domain_name == NULL) {
450 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
451 } else
452 memset(domain_name,0,DOMAIN_SIZE);
453 if(domain_name) {
454 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
455 got_domain = 1;
456 }
457 }
458 }
459 }
460
461 }
462 fclose(fs);
463 SAFE_FREE(line_buf);
464 return 0;
465}
466
467static int get_password_from_file(int file_descript, char * filename)
468{
469 int rc = 0;
470 int i;
471 char c;
472
473 if(mountpassword == NULL)
474 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
475 else
476 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
477
478 if (mountpassword == NULL) {
479 fprintf(stderr, "malloc failed\n");
480 exit(EX_SYSERR);
481 }
482
483 if(filename != NULL) {
484 rc = access(filename, R_OK);
485 if (rc) {
486 fprintf(stderr, "mount.cifs failed: access check of %s failed: %s\n",
487 filename, strerror(errno));
488 exit(EX_SYSERR);
489 }
490 file_descript = open(filename, O_RDONLY);
491 if(file_descript < 0) {
492 fprintf(stderr, "mount.cifs failed. %s attempting to open password file %s\n",
493 strerror(errno),filename);
494 exit(EX_SYSERR);
495 }
496 }
497 /* else file already open and fd provided */
498
499 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
500 rc = read(file_descript,&c,1);
501 if(rc < 0) {
502 fprintf(stderr, "mount.cifs failed. Error %s reading password file\n",strerror(errno));
503 if(filename != NULL)
504 close(file_descript);
505 exit(EX_SYSERR);
506 } else if(rc == 0) {
507 if(mountpassword[0] == 0) {
508 if(verboseflag)
509 fprintf(stderr, "\nWarning: null password used since cifs password file empty");
510 }
511 break;
512 } else /* read valid character */ {
513 if((c == 0) || (c == '\n')) {
514 mountpassword[i] = '\0';
515 break;
516 } else
517 mountpassword[i] = c;
518 }
519 }
520 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
521 fprintf(stderr, "\nWarning: password longer than %d characters specified in cifs password file",
522 MOUNT_PASSWD_SIZE);
523 }
524 got_password = 1;
525 if(filename != NULL) {
526 close(file_descript);
527 }
528
529 return rc;
530}
531
532static int parse_options(char ** optionsp, unsigned long * filesys_flags)
533{
534 const char * data;
535 char * percent_char = NULL;
536 char * value = NULL;
537 char * next_keyword = NULL;
538 char * out = NULL;
539 int out_len = 0;
540 int word_len;
541 int rc = 0;
542 char user[32];
543 char group[32];
544
545 if (!optionsp || !*optionsp)
546 return 1;
547 data = *optionsp;
548
549 /* BB fixme check for separator override BB */
550
551 if (getuid()) {
552 got_uid = 1;
553 snprintf(user,sizeof(user),"%u",getuid());
554 got_gid = 1;
555 snprintf(group,sizeof(group),"%u",getgid());
556 }
557
558/* while ((data = strsep(&options, ",")) != NULL) { */
559 while(data != NULL) {
560 /* check if ends with trailing comma */
561 if(*data == 0)
562 break;
563
564 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
565 /* data = next keyword */
566 /* value = next value ie stuff after equal sign */
567
568 next_keyword = strchr(data,','); /* BB handle sep= */
569
570 /* temporarily null terminate end of keyword=value pair */
571 if(next_keyword)
572 *next_keyword++ = 0;
573
574 /* temporarily null terminate keyword to make keyword and value distinct */
575 if ((value = strchr(data, '=')) != NULL) {
576 *value = '\0';
577 value++;
578 }
579
580 if (strncmp(data, "users",5) == 0) {
581 if(!value || !*value) {
582 *filesys_flags |= MS_USERS;
583 goto nocopy;
584 }
585 } else if (strncmp(data, "user_xattr",10) == 0) {
586 /* do nothing - need to skip so not parsed as user name */
587 } else if (strncmp(data, "user", 4) == 0) {
588
589 if (!value || !*value) {
590 if(data[4] == '\0') {
591 *filesys_flags |= MS_USER;
592 goto nocopy;
593 } else {
594 fprintf(stderr, "username specified with no parameter\n");
595 SAFE_FREE(out);
596 return 1; /* needs_arg; */
597 }
598 } else {
599 if (strnlen(value, 260) < 260) {
600 got_user=1;
601 percent_char = strchr(value,'%');
602 if(percent_char) {
603 *percent_char = ',';
604 if(mountpassword == NULL)
605 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
606 if(mountpassword) {
607 if(got_password)
608 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
609 got_password = 1;
610 percent_char++;
611 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
612 /* remove password from username */
613 while(*percent_char != 0) {
614 *percent_char = ',';
615 percent_char++;
616 }
617 }
618 }
619 /* this is only case in which the user
620 name buf is not malloc - so we have to
621 check for domain name embedded within
622 the user name here since the later
623 call to check_for_domain will not be
624 invoked */
625 domain_name = check_for_domain(&value);
626 } else {
627 fprintf(stderr, "username too long\n");
628 SAFE_FREE(out);
629 return 1;
630 }
631 }
632 } else if (strncmp(data, "pass", 4) == 0) {
633 if (!value || !*value) {
634 if(got_password) {
635 fprintf(stderr, "\npassword specified twice, ignoring second\n");
636 } else
637 got_password = 1;
638 } else if (strnlen(value, MOUNT_PASSWD_SIZE) < MOUNT_PASSWD_SIZE) {
639 if (got_password) {
640 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
641 } else {
642 mountpassword = strndup(value, MOUNT_PASSWD_SIZE);
643 if (!mountpassword) {
644 fprintf(stderr, "mount.cifs error: %s", strerror(ENOMEM));
645 SAFE_FREE(out);
646 return 1;
647 }
648 got_password = 1;
649 }
650 } else {
651 fprintf(stderr, "password too long\n");
652 SAFE_FREE(out);
653 return 1;
654 }
655 goto nocopy;
656 } else if (strncmp(data, "sec", 3) == 0) {
657 if (value) {
658 if (!strncmp(value, "none", 4) ||
659 !strncmp(value, "krb5", 4))
660 got_password = 1;
661 }
662 } else if (strncmp(data, "ip", 2) == 0) {
663 if (!value || !*value) {
664 fprintf(stderr, "target ip address argument missing");
665 } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
666 if(verboseflag)
667 fprintf(stderr, "ip address %s override specified\n",value);
668 got_ip = 1;
669 } else {
670 fprintf(stderr, "ip address too long\n");
671 SAFE_FREE(out);
672 return 1;
673 }
674 } else if ((strncmp(data, "unc", 3) == 0)
675 || (strncmp(data, "target", 6) == 0)
676 || (strncmp(data, "path", 4) == 0)) {
677 if (!value || !*value) {
678 fprintf(stderr, "invalid path to network resource\n");
679 SAFE_FREE(out);
680 return 1; /* needs_arg; */
681 } else if(strnlen(value,5) < 5) {
682 fprintf(stderr, "UNC name too short");
683 }
684
685 if (strnlen(value, 300) < 300) {
686 got_unc = 1;
687 if (strncmp(value, "//", 2) == 0) {
688 if(got_unc)
689 fprintf(stderr, "unc name specified twice, ignoring second\n");
690 else
691 got_unc = 1;
692 } else if (strncmp(value, "\\\\", 2) != 0) {
693 fprintf(stderr, "UNC Path does not begin with // or \\\\ \n");
694 SAFE_FREE(out);
695 return 1;
696 } else {
697 if(got_unc)
698 fprintf(stderr, "unc name specified twice, ignoring second\n");
699 else
700 got_unc = 1;
701 }
702 } else {
703 fprintf(stderr, "CIFS: UNC name too long\n");
704 SAFE_FREE(out);
705 return 1;
706 }
707 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
708 || (strncmp(data, "workg", 5) == 0)) {
709 /* note this allows for synonyms of "domain"
710 such as "DOM" and "dom" and "workgroup"
711 and "WORKGRP" etc. */
712 if (!value || !*value) {
713 fprintf(stderr, "CIFS: invalid domain name\n");
714 SAFE_FREE(out);
715 return 1; /* needs_arg; */
716 }
717 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
718 got_domain = 1;
719 } else {
720 fprintf(stderr, "domain name too long\n");
721 SAFE_FREE(out);
722 return 1;
723 }
724 } else if (strncmp(data, "cred", 4) == 0) {
725 if (value && *value) {
726 rc = open_cred_file(value);
727 if(rc) {
728 fprintf(stderr, "error %d (%s) opening credential file %s\n",
729 rc, strerror(rc), value);
730 SAFE_FREE(out);
731 return 1;
732 }
733 } else {
734 fprintf(stderr, "invalid credential file name specified\n");
735 SAFE_FREE(out);
736 return 1;
737 }
738 } else if (strncmp(data, "uid", 3) == 0) {
739 if (value && *value) {
740 got_uid = 1;
741 if (!isdigit(*value)) {
742 struct passwd *pw;
743
744 if (!(pw = getpwnam(value))) {
745 fprintf(stderr, "bad user name \"%s\"\n", value);
746 exit(EX_USAGE);
747 }
748 snprintf(user, sizeof(user), "%u", pw->pw_uid);
749 } else {
750 strlcpy(user,value,sizeof(user));
751 }
752 }
753 goto nocopy;
754 } else if (strncmp(data, "gid", 3) == 0) {
755 if (value && *value) {
756 got_gid = 1;
757 if (!isdigit(*value)) {
758 struct group *gr;
759
760 if (!(gr = getgrnam(value))) {
761 fprintf(stderr, "bad group name \"%s\"\n", value);
762 exit(EX_USAGE);
763 }
764 snprintf(group, sizeof(group), "%u", gr->gr_gid);
765 } else {
766 strlcpy(group,value,sizeof(group));
767 }
768 }
769 goto nocopy;
770 /* fmask and dmask synonyms for people used to smbfs syntax */
771 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
772 if (!value || !*value) {
773 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
774 SAFE_FREE(out);
775 return 1;
776 }
777
778 if (value[0] != '0') {
779 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
780 }
781
782 if (strcmp (data, "fmask") == 0) {
783 fprintf(stderr, "WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
784 data = "file_mode"; /* BB fix this */
785 }
786 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
787 if (!value || !*value) {
788 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
789 SAFE_FREE(out);
790 return 1;
791 }
792
793 if (value[0] != '0') {
794 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
795 }
796
797 if (strcmp (data, "dmask") == 0) {
798 fprintf(stderr, "WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
799 data = "dir_mode";
800 }
801 /* the following eight mount options should be
802 stripped out from what is passed into the kernel
803 since these eight options are best passed as the
804 mount flags rather than redundantly to the kernel
805 and could generate spurious warnings depending on the
806 level of the corresponding cifs vfs kernel code */
807 } else if (strncmp(data, "nosuid", 6) == 0) {
808 *filesys_flags |= MS_NOSUID;
809 } else if (strncmp(data, "suid", 4) == 0) {
810 *filesys_flags &= ~MS_NOSUID;
811 } else if (strncmp(data, "nodev", 5) == 0) {
812 *filesys_flags |= MS_NODEV;
813 } else if ((strncmp(data, "nobrl", 5) == 0) ||
814 (strncmp(data, "nolock", 6) == 0)) {
815 *filesys_flags &= ~MS_MANDLOCK;
816 } else if (strncmp(data, "dev", 3) == 0) {
817 *filesys_flags &= ~MS_NODEV;
818 } else if (strncmp(data, "noexec", 6) == 0) {
819 *filesys_flags |= MS_NOEXEC;
820 } else if (strncmp(data, "exec", 4) == 0) {
821 *filesys_flags &= ~MS_NOEXEC;
822 } else if (strncmp(data, "guest", 5) == 0) {
823 user_name = (char *)calloc(1, 1);
824 got_user = 1;
825 got_password = 1;
826 } else if (strncmp(data, "ro", 2) == 0) {
827 *filesys_flags |= MS_RDONLY;
828 goto nocopy;
829 } else if (strncmp(data, "rw", 2) == 0) {
830 *filesys_flags &= ~MS_RDONLY;
831 goto nocopy;
832 } else if (strncmp(data, "remount", 7) == 0) {
833 *filesys_flags |= MS_REMOUNT;
834 } /* else if (strnicmp(data, "port", 4) == 0) {
835 if (value && *value) {
836 vol->port =
837 simple_strtoul(value, &value, 0);
838 }
839 } else if (strnicmp(data, "rsize", 5) == 0) {
840 if (value && *value) {
841 vol->rsize =
842 simple_strtoul(value, &value, 0);
843 }
844 } else if (strnicmp(data, "wsize", 5) == 0) {
845 if (value && *value) {
846 vol->wsize =
847 simple_strtoul(value, &value, 0);
848 }
849 } else if (strnicmp(data, "version", 3) == 0) {
850 } else {
851 fprintf(stderr, "CIFS: Unknown mount option %s\n",data);
852 } */ /* nothing to do on those four mount options above.
853 Just pass to kernel and ignore them here */
854
855 /* Copy (possibly modified) option to out */
856 word_len = strlen(data);
857 if (value)
858 word_len += 1 + strlen(value);
859
860 out = (char *)realloc(out, out_len + word_len + 2);
861 if (out == NULL) {
862 perror("malloc");
863 exit(EX_SYSERR);
864 }
865
866 if (out_len) {
867 strlcat(out, ",", out_len + word_len + 2);
868 out_len++;
869 }
870
871 if (value)
872 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
873 else
874 snprintf(out + out_len, word_len + 1, "%s", data);
875 out_len = strlen(out);
876
877nocopy:
878 data = next_keyword;
879 }
880
881 /* special-case the uid and gid */
882 if (got_uid) {
883 word_len = strlen(user);
884
885 out = (char *)realloc(out, out_len + word_len + 6);
886 if (out == NULL) {
887 perror("malloc");
888 exit(EX_SYSERR);
889 }
890
891 if (out_len) {
892 strlcat(out, ",", out_len + word_len + 6);
893 out_len++;
894 }
895 snprintf(out + out_len, word_len + 5, "uid=%s", user);
896 out_len = strlen(out);
897 }
898 if (got_gid) {
899 word_len = strlen(group);
900
901 out = (char *)realloc(out, out_len + 1 + word_len + 6);
902 if (out == NULL) {
903 perror("malloc");
904 exit(EX_SYSERR);
905 }
906
907 if (out_len) {
908 strlcat(out, ",", out_len + word_len + 6);
909 out_len++;
910 }
911 snprintf(out + out_len, word_len + 5, "gid=%s", group);
912 out_len = strlen(out);
913 }
914
915 SAFE_FREE(*optionsp);
916 *optionsp = out;
917 return 0;
918}
919
920/* replace all (one or more) commas with double commas */
921static void check_for_comma(char ** ppasswrd)
922{
923 char *new_pass_buf;
924 char *pass;
925 int i,j;
926 int number_of_commas = 0;
927 int len;
928
929 if(ppasswrd == NULL)
930 return;
931 else
932 (pass = *ppasswrd);
933
934 len = strlen(pass);
935
936 for(i=0;i<len;i++) {
937 if(pass[i] == ',')
938 number_of_commas++;
939 }
940
941 if(number_of_commas == 0)
942 return;
943 if(number_of_commas > MOUNT_PASSWD_SIZE) {
944 /* would otherwise overflow the mount options buffer */
945 fprintf(stderr, "\nInvalid password. Password contains too many commas.\n");
946 return;
947 }
948
949 new_pass_buf = (char *)malloc(len+number_of_commas+1);
950 if(new_pass_buf == NULL)
951 return;
952
953 for(i=0,j=0;i<len;i++,j++) {
954 new_pass_buf[j] = pass[i];
955 if(pass[i] == ',') {
956 j++;
957 new_pass_buf[j] = pass[i];
958 }
959 }
960 new_pass_buf[len+number_of_commas] = 0;
961
962 SAFE_FREE(*ppasswrd);
963 *ppasswrd = new_pass_buf;
964
965 return;
966}
967
968/* Usernames can not have backslash in them and we use
969 [BB check if usernames can have forward slash in them BB]
970 backslash as domain\user separator character
971*/
972static char * check_for_domain(char **ppuser)
973{
974 char * original_string;
975 char * usernm;
976 char * domainnm;
977 int original_len;
978 int len;
979 int i;
980
981 if(ppuser == NULL)
982 return NULL;
983
984 original_string = *ppuser;
985
986 if (original_string == NULL)
987 return NULL;
988
989 original_len = strlen(original_string);
990
991 usernm = strchr(*ppuser,'/');
992 if (usernm == NULL) {
993 usernm = strchr(*ppuser,'\\');
994 if (usernm == NULL)
995 return NULL;
996 }
997
998 if(got_domain) {
999 fprintf(stderr, "Domain name specified twice. Username probably malformed\n");
1000 return NULL;
1001 }
1002
1003 usernm[0] = 0;
1004 domainnm = *ppuser;
1005 if (domainnm[0] != 0) {
1006 got_domain = 1;
1007 } else {
1008 fprintf(stderr, "null domain\n");
1009 }
1010 len = strlen(domainnm);
1011 /* reset domainm to new buffer, and copy
1012 domain name into it */
1013 domainnm = (char *)malloc(len+1);
1014 if(domainnm == NULL)
1015 return NULL;
1016
1017 strlcpy(domainnm,*ppuser,len+1);
1018
1019/* move_string(*ppuser, usernm+1) */
1020 len = strlen(usernm+1);
1021
1022 if(len >= original_len) {
1023 /* should not happen */
1024 return domainnm;
1025 }
1026
1027 for(i=0;i<original_len;i++) {
1028 if(i<len)
1029 original_string[i] = usernm[i+1];
1030 else /* stuff with commas to remove last parm */
1031 original_string[i] = ',';
1032 }
1033
1034 /* BB add check for more than one slash?
1035 strchr(*ppuser,'/');
1036 strchr(*ppuser,'\\')
1037 */
1038
1039 return domainnm;
1040}
1041
1042/* replace all occurances of "from" in a string with "to" */
1043static void replace_char(char *string, char from, char to, int maxlen)
1044{
1045 char *lastchar = string + maxlen;
1046 while (string) {
1047 string = strchr(string, from);
1048 if (string) {
1049 *string = to;
1050 if (string >= lastchar)
1051 return;
1052 }
1053 }
1054}
1055
1056/* Note that caller frees the returned buffer if necessary */
1057static struct addrinfo *
1058parse_server(char ** punc_name)
1059{
1060 char * unc_name = *punc_name;
1061 int length = strnlen(unc_name, MAX_UNC_LEN);
1062 char * share;
1063 struct addrinfo *addrlist;
1064 int rc;
1065
1066 if(length > (MAX_UNC_LEN - 1)) {
1067 fprintf(stderr, "mount error: UNC name too long");
1068 return NULL;
1069 }
1070 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1071 (strncasecmp("smb://", unc_name, 6) == 0)) {
1072 fprintf(stderr, "\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
1073 return NULL;
1074 }
1075
1076 if(length < 3) {
1077 /* BB add code to find DFS root here */
1078 fprintf(stderr, "\nMounting the DFS root for domain not implemented yet\n");
1079 return NULL;
1080 } else {
1081 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
1082 /* check for nfs syntax ie server:share */
1083 share = strchr(unc_name,':');
1084 if(share) {
1085 *punc_name = (char *)malloc(length+3);
1086 if(*punc_name == NULL) {
1087 /* put the original string back if
1088 no memory left */
1089 *punc_name = unc_name;
1090 return NULL;
1091 }
1092 *share = '/';
1093 strlcpy((*punc_name)+2,unc_name,length+1);
1094 SAFE_FREE(unc_name);
1095 unc_name = *punc_name;
1096 unc_name[length+2] = 0;
1097 goto continue_unc_parsing;
1098 } else {
1099 fprintf(stderr, "mount error: improperly formatted UNC name.");
1100 fprintf(stderr, " %s does not begin with \\\\ or //\n",unc_name);
1101 return NULL;
1102 }
1103 } else {
1104continue_unc_parsing:
1105 unc_name[0] = '/';
1106 unc_name[1] = '/';
1107 unc_name += 2;
1108
1109 /* allow for either delimiter between host and sharename */
1110 if ((share = strpbrk(unc_name, "/\\"))) {
1111 *share = 0; /* temporarily terminate the string */
1112 share += 1;
1113 if(got_ip == 0) {
1114 rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
1115 if (rc != 0) {
1116 fprintf(stderr, "mount error: could not resolve address for %s: %s\n",
1117 unc_name, gai_strerror(rc));
1118 addrlist = NULL;
1119 }
1120 }
1121 *(share - 1) = '/'; /* put delimiter back */
1122
1123 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1124 if ((prefixpath = strpbrk(share, "/\\"))) {
1125 *prefixpath = 0; /* permanently terminate the string */
1126 if (!strlen(++prefixpath))
1127 prefixpath = NULL; /* this needs to be done explicitly */
1128 }
1129 if(got_ip) {
1130 if(verboseflag)
1131 fprintf(stderr, "ip address specified explicitly\n");
1132 return NULL;
1133 }
1134 /* BB should we pass an alternate version of the share name as Unicode */
1135
1136 return addrlist;
1137 } else {
1138 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1139 fprintf(stderr, "Mounting the DFS root for a particular server not implemented yet\n");
1140 return NULL;
1141 }
1142 }
1143 }
1144}
1145
1146static struct option longopts[] = {
1147 { "all", 0, NULL, 'a' },
1148 { "help",0, NULL, 'h' },
1149 { "move",0, NULL, 'm' },
1150 { "bind",0, NULL, 'b' },
1151 { "read-only", 0, NULL, 'r' },
1152 { "ro", 0, NULL, 'r' },
1153 { "verbose", 0, NULL, 'v' },
1154 { "version", 0, NULL, 'V' },
1155 { "read-write", 0, NULL, 'w' },
1156 { "rw", 0, NULL, 'w' },
1157 { "options", 1, NULL, 'o' },
1158 { "type", 1, NULL, 't' },
1159 { "rsize",1, NULL, 'R' },
1160 { "wsize",1, NULL, 'W' },
1161 { "uid", 1, NULL, '1'},
1162 { "gid", 1, NULL, '2'},
1163 { "user",1,NULL,'u'},
1164 { "username",1,NULL,'u'},
1165 { "dom",1,NULL,'d'},
1166 { "domain",1,NULL,'d'},
1167 { "password",1,NULL,'p'},
1168 { "pass",1,NULL,'p'},
1169 { "credentials",1,NULL,'c'},
1170 { "port",1,NULL,'P'},
1171 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1172 { NULL, 0, NULL, 0 }
1173};
1174
1175/* convert a string to uppercase. return false if the string
1176 * wasn't ASCII. Return success on a NULL ptr */
1177static int
1178uppercase_string(char *string)
1179{
1180 if (!string)
1181 return 1;
1182
1183 while (*string) {
1184 /* check for unicode */
1185 if ((unsigned char) string[0] & 0x80)
1186 return 0;
1187 *string = toupper((unsigned char) *string);
1188 string++;
1189 }
1190
1191 return 1;
1192}
1193
1194static void print_cifs_mount_version(void)
1195{
1196 printf("mount.cifs version: %s.%s%s\n",
1197 MOUNT_CIFS_VERSION_MAJOR,
1198 MOUNT_CIFS_VERSION_MINOR,
1199 MOUNT_CIFS_VENDOR_SUFFIX);
1200}
1201
1202/*
1203 * This function borrowed from fuse-utils...
1204 *
1205 * glibc's addmntent (at least as of 2.10 or so) doesn't properly encode
1206 * newlines embedded within the text fields. To make sure no one corrupts
1207 * the mtab, fail the mount if there are embedded newlines.
1208 */
1209static int check_newline(const char *progname, const char *name)
1210{
1211 char *s;
1212 for (s = "\n"; *s; s++) {
1213 if (strchr(name, *s)) {
1214 fprintf(stderr, "%s: illegal character 0x%02x in mount entry\n",
1215 progname, *s);
1216 return EX_USAGE;
1217 }
1218 }
1219 return 0;
1220}
1221
1222static int check_mtab(const char *progname, const char *devname,
1223 const char *dir)
1224{
1225 if (check_newline(progname, devname) == -1 ||
1226 check_newline(progname, dir) == -1)
1227 return EX_USAGE;
1228 return 0;
1229}
1230
1231
1232int main(int argc, char ** argv)
1233{
1234 int c;
1235 unsigned long flags = MS_MANDLOCK;
1236 char * orgoptions = NULL;
1237 char * share_name = NULL;
1238 const char * ipaddr = NULL;
1239 char * uuid = NULL;
1240 char * mountpoint = NULL;
1241 char * options = NULL;
1242 char * optionstail;
1243 char * resolved_path = NULL;
1244 char * temp;
1245 char * dev_name;
1246 int rc = 0;
1247 int rsize = 0;
1248 int wsize = 0;
1249 int nomtab = 0;
1250 int uid = 0;
1251 int gid = 0;
1252 int optlen = 0;
1253 int orgoptlen = 0;
1254 size_t options_size = 0;
1255 size_t current_len;
1256 int retry = 0; /* set when we have to retry mount with uppercase */
1257 struct addrinfo *addrhead = NULL, *addr;
1258 struct utsname sysinfo;
1259 struct mntent mountent;
1260 struct sockaddr_in *addr4;
1261 struct sockaddr_in6 *addr6;
1262 FILE * pmntfile;
1263
1264 if (check_setuid())
1265 return EX_USAGE;
1266
1267 /* setlocale(LC_ALL, "");
1268 bindtextdomain(PACKAGE, LOCALEDIR);
1269 textdomain(PACKAGE); */
1270
1271 if(argc && argv)
1272 thisprogram = argv[0];
1273 else
1274 mount_cifs_usage(stderr);
1275
1276 if(thisprogram == NULL)
1277 thisprogram = "mount.cifs";
1278
1279 uname(&sysinfo);
1280 /* BB add workstation name and domain and pass down */
1281
1282/* #ifdef _GNU_SOURCE
1283 fprintf(stderr, " node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1284#endif */
1285 if(argc > 2) {
1286 dev_name = argv[1];
1287 share_name = strndup(argv[1], MAX_UNC_LEN);
1288 if (share_name == NULL) {
1289 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1290 exit(EX_SYSERR);
1291 }
1292 mountpoint = argv[2];
1293 } else if (argc == 2) {
1294 if ((strcmp(argv[1], "-V") == 0) ||
1295 (strcmp(argv[1], "--version") == 0))
1296 {
1297 print_cifs_mount_version();
1298 exit(0);
1299 }
1300
1301 if ((strcmp(argv[1], "-h") == 0) ||
1302 (strcmp(argv[1], "-?") == 0) ||
1303 (strcmp(argv[1], "--help") == 0))
1304 mount_cifs_usage(stdout);
1305
1306 mount_cifs_usage(stderr);
1307 } else {
1308 mount_cifs_usage(stderr);
1309 }
1310
1311
1312 /* add sharename in opts string as unc= parm */
1313 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1314 longopts, NULL)) != -1) {
1315 switch (c) {
1316/* No code to do the following options yet */
1317/* case 'l':
1318 list_with_volumelabel = 1;
1319 break;
1320 case 'L':
1321 volumelabel = optarg;
1322 break; */
1323/* case 'a':
1324 ++mount_all;
1325 break; */
1326
1327 case '?':
1328 case 'h': /* help */
1329 mount_cifs_usage(stdout);
1330 case 'n':
1331 ++nomtab;
1332 break;
1333 case 'b':
1334#ifdef MS_BIND
1335 flags |= MS_BIND;
1336#else
1337 fprintf(stderr,
1338 "option 'b' (MS_BIND) not supported\n");
1339#endif
1340 break;
1341 case 'm':
1342#ifdef MS_MOVE
1343 flags |= MS_MOVE;
1344#else
1345 fprintf(stderr,
1346 "option 'm' (MS_MOVE) not supported\n");
1347#endif
1348 break;
1349 case 'o':
1350 orgoptions = strdup(optarg);
1351 break;
1352 case 'r': /* mount readonly */
1353 flags |= MS_RDONLY;
1354 break;
1355 case 'U':
1356 uuid = optarg;
1357 break;
1358 case 'v':
1359 ++verboseflag;
1360 break;
1361 case 'V':
1362 print_cifs_mount_version();
1363 exit (0);
1364 case 'w':
1365 flags &= ~MS_RDONLY;
1366 break;
1367 case 'R':
1368 rsize = atoi(optarg) ;
1369 break;
1370 case 'W':
1371 wsize = atoi(optarg);
1372 break;
1373 case '1':
1374 if (isdigit(*optarg)) {
1375 char *ep;
1376
1377 uid = strtoul(optarg, &ep, 10);
1378 if (*ep) {
1379 fprintf(stderr, "bad uid value \"%s\"\n", optarg);
1380 exit(EX_USAGE);
1381 }
1382 } else {
1383 struct passwd *pw;
1384
1385 if (!(pw = getpwnam(optarg))) {
1386 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1387 exit(EX_USAGE);
1388 }
1389 uid = pw->pw_uid;
1390 endpwent();
1391 }
1392 break;
1393 case '2':
1394 if (isdigit(*optarg)) {
1395 char *ep;
1396
1397 gid = strtoul(optarg, &ep, 10);
1398 if (*ep) {
1399 fprintf(stderr, "bad gid value \"%s\"\n", optarg);
1400 exit(EX_USAGE);
1401 }
1402 } else {
1403 struct group *gr;
1404
1405 if (!(gr = getgrnam(optarg))) {
1406 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1407 exit(EX_USAGE);
1408 }
1409 gid = gr->gr_gid;
1410 endpwent();
1411 }
1412 break;
1413 case 'u':
1414 got_user = 1;
1415 user_name = optarg;
1416 break;
1417 case 'd':
1418 domain_name = optarg; /* BB fix this - currently ignored */
1419 got_domain = 1;
1420 break;
1421 case 'p':
1422 if(mountpassword == NULL)
1423 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1424 if(mountpassword) {
1425 got_password = 1;
1426 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1427 }
1428 break;
1429 case 'S':
1430 get_password_from_file(0 /* stdin */,NULL);
1431 break;
1432 case 't':
1433 break;
1434 case 'f':
1435 ++fakemnt;
1436 break;
1437 default:
1438 fprintf(stderr, "unknown mount option %c\n",c);
1439 mount_cifs_usage(stderr);
1440 }
1441 }
1442
1443 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1444 mount_cifs_usage(stderr);
1445 }
1446
1447 /* make sure mountpoint is legit */
1448 rc = chdir(mountpoint);
1449 if (rc) {
1450 fprintf(stderr, "Couldn't chdir to %s: %s\n", mountpoint,
1451 strerror(errno));
1452 rc = EX_USAGE;
1453 goto mount_exit;
1454 }
1455
1456 rc = check_mountpoint(thisprogram, mountpoint);
1457 if (rc)
1458 goto mount_exit;
1459
1460 /* sanity check for unprivileged mounts */
1461 if (getuid()) {
1462 rc = check_fstab(thisprogram, mountpoint, dev_name,
1463 &orgoptions);
1464 if (rc)
1465 goto mount_exit;
1466
1467 /* enable any default user mount flags */
1468 flags |= CIFS_SETUID_FLAGS;
1469 }
1470
1471 if (getenv("PASSWD")) {
1472 if(mountpassword == NULL)
1473 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1474 if(mountpassword) {
1475 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1476 got_password = 1;
1477 }
1478 } else if (getenv("PASSWD_FD")) {
1479 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1480 } else if (getenv("PASSWD_FILE")) {
1481 get_password_from_file(0, getenv("PASSWD_FILE"));
1482 }
1483
1484 if (orgoptions && parse_options(&orgoptions, &flags)) {
1485 rc = EX_USAGE;
1486 goto mount_exit;
1487 }
1488
1489 if (getuid()) {
1490#if !CIFS_LEGACY_SETUID_CHECK
1491 if (!(flags & (MS_USERS|MS_USER))) {
1492 fprintf(stderr, "%s: permission denied\n", thisprogram);
1493 rc = EX_USAGE;
1494 goto mount_exit;
1495 }
1496#endif /* !CIFS_LEGACY_SETUID_CHECK */
1497
1498 if (geteuid()) {
1499 fprintf(stderr, "%s: not installed setuid - \"user\" "
1500 "CIFS mounts not supported.",
1501 thisprogram);
1502 rc = EX_FAIL;
1503 goto mount_exit;
1504 }
1505 }
1506
1507 flags &= ~(MS_USERS|MS_USER);
1508
1509 addrhead = addr = parse_server(&share_name);
1510 if((addrhead == NULL) && (got_ip == 0)) {
1511 fprintf(stderr, "No ip address specified and hostname not found\n");
1512 rc = EX_USAGE;
1513 goto mount_exit;
1514 }
1515
1516 /* BB save off path and pop after mount returns? */
1517 resolved_path = (char *)malloc(PATH_MAX+1);
1518 if (!resolved_path) {
1519 fprintf(stderr, "Unable to allocate memory.\n");
1520 rc = EX_SYSERR;
1521 goto mount_exit;
1522 }
1523
1524 /* Note that if we can not canonicalize the name, we get
1525 another chance to see if it is valid when we chdir to it */
1526 if(!realpath(".", resolved_path)) {
1527 fprintf(stderr, "Unable to resolve %s to canonical path: %s\n",
1528 mountpoint, strerror(errno));
1529 rc = EX_SYSERR;
1530 goto mount_exit;
1531 }
1532
1533 mountpoint = resolved_path;
1534
1535 if(got_user == 0) {
1536 /* Note that the password will not be retrieved from the
1537 USER env variable (ie user%password form) as there is
1538 already a PASSWD environment varaible */
1539 if (getenv("USER"))
1540 user_name = strdup(getenv("USER"));
1541 if (user_name == NULL)
1542 user_name = getusername();
1543 got_user = 1;
1544 }
1545
1546 if(got_password == 0) {
1547 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1548 no good replacement yet. */
1549 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1550 if (!tmp_pass || !mountpassword) {
1551 fprintf(stderr, "Password not entered, exiting\n");
1552 exit(EX_USAGE);
1553 }
1554 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1555 got_password = 1;
1556 }
1557 /* FIXME launch daemon (handles dfs name resolution and credential change)
1558 remember to clear parms and overwrite password field before launching */
1559 if(orgoptions) {
1560 optlen = strlen(orgoptions);
1561 orgoptlen = optlen;
1562 } else
1563 optlen = 0;
1564 if(share_name)
1565 optlen += strlen(share_name) + 4;
1566 else {
1567 fprintf(stderr, "No server share name specified\n");
1568 fprintf(stderr, "\nMounting the DFS root for server not implemented yet\n");
1569 exit(EX_USAGE);
1570 }
1571 if(user_name)
1572 optlen += strlen(user_name) + 6;
1573 optlen += MAX_ADDRESS_LEN + 4;
1574 if(mountpassword)
1575 optlen += strlen(mountpassword) + 6;
1576mount_retry:
1577 SAFE_FREE(options);
1578 options_size = optlen + 10 + DOMAIN_SIZE;
1579 options = (char *)malloc(options_size /* space for commas in password */ + 8 /* space for domain= , domain name itself was counted as part of the length username string above */);
1580
1581 if(options == NULL) {
1582 fprintf(stderr, "Could not allocate memory for mount options\n");
1583 exit(EX_SYSERR);
1584 }
1585
1586 strlcpy(options, "unc=", options_size);
1587 strlcat(options,share_name,options_size);
1588 /* scan backwards and reverse direction of slash */
1589 temp = strrchr(options, '/');
1590 if(temp > options + 6)
1591 *temp = '\\';
1592 if(user_name) {
1593 /* check for syntax like user=domain\user */
1594 if(got_domain == 0)
1595 domain_name = check_for_domain(&user_name);
1596 strlcat(options,",user=",options_size);
1597 strlcat(options,user_name,options_size);
1598 }
1599 if(retry == 0) {
1600 if(domain_name) {
1601 /* extra length accounted for in option string above */
1602 strlcat(options,",domain=",options_size);
1603 strlcat(options,domain_name,options_size);
1604 }
1605 }
1606
1607 strlcat(options,",ver=",options_size);
1608 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1609
1610 if(orgoptions) {
1611 strlcat(options,",",options_size);
1612 strlcat(options,orgoptions,options_size);
1613 }
1614 if(prefixpath) {
1615 strlcat(options,",prefixpath=",options_size);
1616 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1617 }
1618
1619 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1620 replace_char(dev_name, '\\', '/', strlen(share_name));
1621
1622 if (!got_ip && addr) {
1623 strlcat(options, ",ip=", options_size);
1624 current_len = strnlen(options, options_size);
1625 optionstail = options + current_len;
1626 switch (addr->ai_addr->sa_family) {
1627 case AF_INET6:
1628 addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1629 ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1630 options_size - current_len);
1631 break;
1632 case AF_INET:
1633 addr4 = (struct sockaddr_in *) addr->ai_addr;
1634 ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1635 options_size - current_len);
1636 break;
1637 default:
1638 ipaddr = NULL;
1639 }
1640
1641 /* if the address looks bogus, try the next one */
1642 if (!ipaddr) {
1643 addr = addr->ai_next;
1644 if (addr)
1645 goto mount_retry;
1646 rc = EX_SYSERR;
1647 goto mount_exit;
1648 }
1649 }
1650
1651 if (addr && addr->ai_addr->sa_family == AF_INET6 && addr6->sin6_scope_id) {
1652 strlcat(options, "%", options_size);
1653 current_len = strnlen(options, options_size);
1654 optionstail = options + current_len;
1655 snprintf(optionstail, options_size - current_len, "%u",
1656 addr6->sin6_scope_id);
1657 }
1658
1659 if(verboseflag)
1660 fprintf(stderr, "\nmount.cifs kernel mount options: %s", options);
1661
1662 if (mountpassword) {
1663 /*
1664 * Commas have to be doubled, or else they will
1665 * look like the parameter separator
1666 */
1667 if(retry == 0)
1668 check_for_comma(&mountpassword);
1669 strlcat(options,",pass=",options_size);
1670 strlcat(options,mountpassword,options_size);
1671 if (verboseflag)
1672 fprintf(stderr, ",pass=********");
1673 }
1674
1675 if (verboseflag)
1676 fprintf(stderr, "\n");
1677
1678 rc = check_mtab(thisprogram, dev_name, mountpoint);
1679 if (rc)
1680 goto mount_exit;
1681
1682 if (!fakemnt && mount(dev_name, ".", "cifs", flags, options)) {
1683 switch (errno) {
1684 case ECONNREFUSED:
1685 case EHOSTUNREACH:
1686 if (addr) {
1687 addr = addr->ai_next;
1688 if (addr)
1689 goto mount_retry;
1690 }
1691 break;
1692 case ENODEV:
1693 fprintf(stderr, "mount error: cifs filesystem not supported by the system\n");
1694 break;
1695 case ENXIO:
1696 if(retry == 0) {
1697 retry = 1;
1698 if (uppercase_string(dev_name) &&
1699 uppercase_string(share_name) &&
1700 uppercase_string(prefixpath)) {
1701 fprintf(stderr, "retrying with upper case share name\n");
1702 goto mount_retry;
1703 }
1704 }
1705 }
1706 fprintf(stderr, "mount error(%d): %s\n", errno, strerror(errno));
1707 fprintf(stderr, "Refer to the mount.cifs(8) manual page (e.g. man "
1708 "mount.cifs)\n");
1709 rc = EX_FAIL;
1710 goto mount_exit;
1711 }
1712
1713 if (nomtab)
1714 goto mount_exit;
1715 atexit(unlock_mtab);
1716 rc = lock_mtab();
1717 if (rc) {
1718 fprintf(stderr, "cannot lock mtab");
1719 goto mount_exit;
1720 }
1721 pmntfile = setmntent(MOUNTED, "a+");
1722 if (!pmntfile) {
1723 fprintf(stderr, "could not update mount table\n");
1724 unlock_mtab();
1725 rc = EX_FILEIO;
1726 goto mount_exit;
1727 }
1728 mountent.mnt_fsname = dev_name;
1729 mountent.mnt_dir = mountpoint;
1730 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1731 mountent.mnt_opts = (char *)malloc(220);
1732 if(mountent.mnt_opts) {
1733 char * mount_user = getusername();
1734 memset(mountent.mnt_opts,0,200);
1735 if(flags & MS_RDONLY)
1736 strlcat(mountent.mnt_opts,"ro",220);
1737 else
1738 strlcat(mountent.mnt_opts,"rw",220);
1739 if(flags & MS_MANDLOCK)
1740 strlcat(mountent.mnt_opts,",mand",220);
1741 if(flags & MS_NOEXEC)
1742 strlcat(mountent.mnt_opts,",noexec",220);
1743 if(flags & MS_NOSUID)
1744 strlcat(mountent.mnt_opts,",nosuid",220);
1745 if(flags & MS_NODEV)
1746 strlcat(mountent.mnt_opts,",nodev",220);
1747 if(flags & MS_SYNCHRONOUS)
1748 strlcat(mountent.mnt_opts,",sync",220);
1749 if(mount_user) {
1750 if(getuid() != 0) {
1751 strlcat(mountent.mnt_opts,
1752 ",user=", 220);
1753 strlcat(mountent.mnt_opts,
1754 mount_user, 220);
1755 }
1756 }
1757 }
1758 mountent.mnt_freq = 0;
1759 mountent.mnt_passno = 0;
1760 rc = addmntent(pmntfile,&mountent);
1761 endmntent(pmntfile);
1762 unlock_mtab();
1763 SAFE_FREE(mountent.mnt_opts);
1764 if (rc)
1765 rc = EX_FILEIO;
1766mount_exit:
1767 if(mountpassword) {
1768 int len = strlen(mountpassword);
1769 memset(mountpassword,0,len);
1770 SAFE_FREE(mountpassword);
1771 }
1772
1773 if (addrhead)
1774 freeaddrinfo(addrhead);
1775 SAFE_FREE(options);
1776 SAFE_FREE(orgoptions);
1777 SAFE_FREE(resolved_path);
1778 SAFE_FREE(share_name);
1779 exit(rc);
1780}
Note: See TracBrowser for help on using the repository browser.