Fix CID: 456 - resource leak on function exit.
[Samba.git] / source / client / mount.cifs.c
blobce13b881982792f7e38df1c0233957ab083e25d5
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)
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.
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.
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/>. */
19 #ifndef _GNU_SOURCE
20 #define _GNU_SOURCE
21 #endif
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>
43 #define MOUNT_CIFS_VERSION_MAJOR "1"
44 #define MOUNT_CIFS_VERSION_MINOR "11"
46 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
47 #ifdef _SAMBA_BUILD_
48 #include "include/version.h"
49 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
50 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
51 #else
52 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
53 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
54 #else
55 #define MOUNT_CIFS_VENDOR_SUFFIX ""
56 #endif /* _SAMBA_BUILD_ */
57 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
59 #ifdef _SAMBA_BUILD_
60 #include "include/config.h"
61 #endif
63 #ifndef MS_MOVE
64 #define MS_MOVE 8192
65 #endif
67 #ifndef MS_BIND
68 #define MS_BIND 4096
69 #endif
71 #define MAX_UNC_LEN 1024
73 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
75 #ifndef SAFE_FREE
76 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
77 #endif
79 #define MOUNT_PASSWD_SIZE 64
80 #define DOMAIN_SIZE 64
82 /* exit status - bits below are ORed */
83 #define EX_USAGE 1 /* incorrect invocation or permission */
84 #define EX_SYSERR 2 /* out of memory, cannot fork, ... */
85 #define EX_SOFTWARE 4 /* internal mount bug or wrong version */
86 #define EX_USER 8 /* user interrupt */
87 #define EX_FILEIO 16 /* problems writing, locking, ... mtab/fstab */
88 #define EX_FAIL 32 /* mount failure */
89 #define EX_SOMEOK 64 /* some mount succeeded */
91 const char *thisprogram;
92 int verboseflag = 0;
93 static int got_password = 0;
94 static int got_user = 0;
95 static int got_domain = 0;
96 static int got_ip = 0;
97 static int got_unc = 0;
98 static int got_uid = 0;
99 static int got_gid = 0;
100 static char * user_name = NULL;
101 static char * mountpassword = NULL;
102 char * domain_name = NULL;
103 char * prefixpath = NULL;
105 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
106 * don't link to libreplace so need them here. */
108 /* like strncpy but does not 0 fill the buffer and always null
109 * terminates. bufsize is the size of the destination buffer */
111 #ifndef HAVE_STRLCPY
112 static size_t strlcpy(char *d, const char *s, size_t bufsize)
114 size_t len = strlen(s);
115 size_t ret = len;
116 if (bufsize <= 0) return 0;
117 if (len >= bufsize) len = bufsize-1;
118 memcpy(d, s, len);
119 d[len] = 0;
120 return ret;
122 #endif
124 /* like strncat but does not 0 fill the buffer and always null
125 * terminates. bufsize is the length of the buffer, which should
126 * be one more than the maximum resulting string length */
128 #ifndef HAVE_STRLCAT
129 static size_t strlcat(char *d, const char *s, size_t bufsize)
131 size_t len1 = strlen(d);
132 size_t len2 = strlen(s);
133 size_t ret = len1 + len2;
135 if (len1+len2 >= bufsize) {
136 if (bufsize < (len1+1)) {
137 return ret;
139 len2 = bufsize - (len1+1);
141 if (len2 > 0) {
142 memcpy(d+len1, s, len2);
143 d[len1+len2] = 0;
145 return ret;
147 #endif
149 /* BB finish BB
151 cifs_umount
152 open nofollow - avoid symlink exposure?
153 get owner of dir see if matches self or if root
154 call system(umount argv) etc.
156 BB end finish BB */
158 static char * check_for_domain(char **);
161 static void mount_cifs_usage(void)
163 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
164 printf("\nMount the remote target, specified as a UNC name,");
165 printf(" to a local directory.\n\nOptions:\n");
166 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
167 printf("\nLess commonly used options:");
168 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
169 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
170 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
171 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
172 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
173 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
174 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
175 printf("\n\nRarely used options:");
176 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
177 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
178 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
179 printf("\n\tin6_addr");
180 printf("\n\nOptions are described in more detail in the manual page");
181 printf("\n\tman 8 mount.cifs\n");
182 printf("\nTo display the version number of the mount helper:");
183 printf("\n\t%s -V\n",thisprogram);
185 SAFE_FREE(mountpassword);
186 exit(EX_USAGE);
189 /* caller frees username if necessary */
190 static char * getusername(void) {
191 char *username = NULL;
192 struct passwd *password = getpwuid(getuid());
194 if (password) {
195 username = password->pw_name;
197 return username;
200 static char * parse_cifs_url(char * unc_name)
202 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
203 return NULL;
206 static int open_cred_file(char * file_name)
208 char * line_buf;
209 char * temp_val;
210 FILE * fs;
211 int i, length;
212 fs = fopen(file_name,"r");
213 if(fs == NULL)
214 return errno;
215 line_buf = (char *)malloc(4096);
216 if(line_buf == NULL) {
217 fclose(fs);
218 return ENOMEM;
221 while(fgets(line_buf,4096,fs)) {
222 /* parse line from credential file */
224 /* eat leading white space */
225 for(i=0;i<4086;i++) {
226 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
227 break;
228 /* if whitespace - skip past it */
230 if (strncasecmp("username",line_buf+i,8) == 0) {
231 temp_val = strchr(line_buf + i,'=');
232 if(temp_val) {
233 /* go past equals sign */
234 temp_val++;
235 for(length = 0;length<4087;length++) {
236 if ((temp_val[length] == '\n')
237 || (temp_val[length] == '\0')) {
238 temp_val[length] = '\0';
239 break;
242 if(length > 4086) {
243 printf("mount.cifs failed due to malformed username in credentials file");
244 memset(line_buf,0,4096);
245 exit(EX_USAGE);
246 } else {
247 got_user = 1;
248 user_name = (char *)calloc(1 + length,1);
249 /* BB adding free of user_name string before exit,
250 not really necessary but would be cleaner */
251 strlcpy(user_name,temp_val, length+1);
254 } else if (strncasecmp("password",line_buf+i,8) == 0) {
255 temp_val = strchr(line_buf+i,'=');
256 if(temp_val) {
257 /* go past equals sign */
258 temp_val++;
259 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
260 if ((temp_val[length] == '\n')
261 || (temp_val[length] == '\0')) {
262 temp_val[length] = '\0';
263 break;
266 if(length > MOUNT_PASSWD_SIZE) {
267 printf("mount.cifs failed: password in credentials file too long\n");
268 memset(line_buf,0, 4096);
269 exit(EX_USAGE);
270 } else {
271 if(mountpassword == NULL) {
272 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
273 } else
274 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
275 if(mountpassword) {
276 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
277 got_password = 1;
281 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
282 temp_val = strchr(line_buf+i,'=');
283 if(temp_val) {
284 /* go past equals sign */
285 temp_val++;
286 if(verboseflag)
287 printf("\nDomain %s\n",temp_val);
288 for(length = 0;length<DOMAIN_SIZE+1;length++) {
289 if ((temp_val[length] == '\n')
290 || (temp_val[length] == '\0')) {
291 temp_val[length] = '\0';
292 break;
295 if(length > DOMAIN_SIZE) {
296 printf("mount.cifs failed: domain in credentials file too long\n");
297 exit(EX_USAGE);
298 } else {
299 if(domain_name == NULL) {
300 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
301 } else
302 memset(domain_name,0,DOMAIN_SIZE);
303 if(domain_name) {
304 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
305 got_domain = 1;
312 fclose(fs);
313 SAFE_FREE(line_buf);
314 return 0;
317 static int get_password_from_file(int file_descript, char * filename)
319 int rc = 0;
320 int i;
321 char c;
323 if(mountpassword == NULL)
324 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
325 else
326 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
328 if (mountpassword == NULL) {
329 printf("malloc failed\n");
330 exit(EX_SYSERR);
333 if(filename != NULL) {
334 file_descript = open(filename, O_RDONLY);
335 if(file_descript < 0) {
336 printf("mount.cifs failed. %s attempting to open password file %s\n",
337 strerror(errno),filename);
338 exit(EX_SYSERR);
341 /* else file already open and fd provided */
343 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
344 rc = read(file_descript,&c,1);
345 if(rc < 0) {
346 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
347 if(filename != NULL)
348 close(file_descript);
349 exit(EX_SYSERR);
350 } else if(rc == 0) {
351 if(mountpassword[0] == 0) {
352 if(verboseflag)
353 printf("\nWarning: null password used since cifs password file empty");
355 break;
356 } else /* read valid character */ {
357 if((c == 0) || (c == '\n')) {
358 mountpassword[i] = '\0';
359 break;
360 } else
361 mountpassword[i] = c;
364 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
365 printf("\nWarning: password longer than %d characters specified in cifs password file",
366 MOUNT_PASSWD_SIZE);
368 got_password = 1;
369 if(filename != NULL) {
370 close(file_descript);
373 return rc;
376 static int parse_options(char ** optionsp, int * filesys_flags)
378 const char * data;
379 char * percent_char = NULL;
380 char * value = NULL;
381 char * next_keyword = NULL;
382 char * out = NULL;
383 int out_len = 0;
384 int word_len;
385 int rc = 0;
386 char user[32];
387 char group[32];
389 if (!optionsp || !*optionsp)
390 return 1;
391 data = *optionsp;
393 if(verboseflag)
394 printf("parsing options: %s\n", data);
396 /* BB fixme check for separator override BB */
398 if (getuid()) {
399 got_uid = 1;
400 snprintf(user,sizeof(user),"%u",getuid());
401 got_gid = 1;
402 snprintf(group,sizeof(group),"%u",getgid());
405 /* while ((data = strsep(&options, ",")) != NULL) { */
406 while(data != NULL) {
407 /* check if ends with trailing comma */
408 if(*data == 0)
409 break;
411 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
412 /* data = next keyword */
413 /* value = next value ie stuff after equal sign */
415 next_keyword = strchr(data,','); /* BB handle sep= */
417 /* temporarily null terminate end of keyword=value pair */
418 if(next_keyword)
419 *next_keyword++ = 0;
421 /* temporarily null terminate keyword to make keyword and value distinct */
422 if ((value = strchr(data, '=')) != NULL) {
423 *value = '\0';
424 value++;
427 if (strncmp(data, "users",5) == 0) {
428 if(!value || !*value) {
429 goto nocopy;
431 } else if (strncmp(data, "user_xattr",10) == 0) {
432 /* do nothing - need to skip so not parsed as user name */
433 } else if (strncmp(data, "user", 4) == 0) {
435 if (!value || !*value) {
436 if(data[4] == '\0') {
437 if(verboseflag)
438 printf("\nskipping empty user mount parameter\n");
439 /* remove the parm since it would otherwise be confusing
440 to the kernel code which would think it was a real username */
441 goto nocopy;
442 } else {
443 printf("username specified with no parameter\n");
444 SAFE_FREE(out);
445 return 1; /* needs_arg; */
447 } else {
448 if (strnlen(value, 260) < 260) {
449 got_user=1;
450 percent_char = strchr(value,'%');
451 if(percent_char) {
452 *percent_char = ',';
453 if(mountpassword == NULL)
454 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
455 if(mountpassword) {
456 if(got_password)
457 printf("\nmount.cifs warning - password specified twice\n");
458 got_password = 1;
459 percent_char++;
460 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
461 /* remove password from username */
462 while(*percent_char != 0) {
463 *percent_char = ',';
464 percent_char++;
468 /* this is only case in which the user
469 name buf is not malloc - so we have to
470 check for domain name embedded within
471 the user name here since the later
472 call to check_for_domain will not be
473 invoked */
474 domain_name = check_for_domain(&value);
475 } else {
476 printf("username too long\n");
477 SAFE_FREE(out);
478 return 1;
481 } else if (strncmp(data, "pass", 4) == 0) {
482 if (!value || !*value) {
483 if(got_password) {
484 printf("\npassword specified twice, ignoring second\n");
485 } else
486 got_password = 1;
487 } else if (strnlen(value, 17) < 17) {
488 if(got_password)
489 printf("\nmount.cifs warning - password specified twice\n");
490 got_password = 1;
491 } else {
492 printf("password too long\n");
493 SAFE_FREE(out);
494 return 1;
496 } else if (strncmp(data, "sec", 3) == 0) {
497 if (value) {
498 if (!strcmp(value, "none"))
499 got_password = 1;
501 } else if (strncmp(data, "ip", 2) == 0) {
502 if (!value || !*value) {
503 printf("target ip address argument missing");
504 } else if (strnlen(value, 35) < 35) {
505 if(verboseflag)
506 printf("ip address %s override specified\n",value);
507 got_ip = 1;
508 } else {
509 printf("ip address too long\n");
510 SAFE_FREE(out);
511 return 1;
513 } else if ((strncmp(data, "unc", 3) == 0)
514 || (strncmp(data, "target", 6) == 0)
515 || (strncmp(data, "path", 4) == 0)) {
516 if (!value || !*value) {
517 printf("invalid path to network resource\n");
518 SAFE_FREE(out);
519 return 1; /* needs_arg; */
520 } else if(strnlen(value,5) < 5) {
521 printf("UNC name too short");
524 if (strnlen(value, 300) < 300) {
525 got_unc = 1;
526 if (strncmp(value, "//", 2) == 0) {
527 if(got_unc)
528 printf("unc name specified twice, ignoring second\n");
529 else
530 got_unc = 1;
531 } else if (strncmp(value, "\\\\", 2) != 0) {
532 printf("UNC Path does not begin with // or \\\\ \n");
533 SAFE_FREE(out);
534 return 1;
535 } else {
536 if(got_unc)
537 printf("unc name specified twice, ignoring second\n");
538 else
539 got_unc = 1;
541 } else {
542 printf("CIFS: UNC name too long\n");
543 SAFE_FREE(out);
544 return 1;
546 } else if ((strncmp(data, "domain", 3) == 0)
547 || (strncmp(data, "workgroup", 5) == 0)) {
548 if (!value || !*value) {
549 printf("CIFS: invalid domain name\n");
550 SAFE_FREE(out);
551 return 1; /* needs_arg; */
553 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
554 got_domain = 1;
555 } else {
556 printf("domain name too long\n");
557 SAFE_FREE(out);
558 return 1;
560 } else if (strncmp(data, "cred", 4) == 0) {
561 if (value && *value) {
562 rc = open_cred_file(value);
563 if(rc) {
564 printf("error %d (%s) opening credential file %s\n",
565 rc, strerror(rc), value);
566 SAFE_FREE(out);
567 return 1;
569 } else {
570 printf("invalid credential file name specified\n");
571 SAFE_FREE(out);
572 return 1;
574 } else if (strncmp(data, "uid", 3) == 0) {
575 if (value && *value) {
576 got_uid = 1;
577 if (!isdigit(*value)) {
578 struct passwd *pw;
580 if (!(pw = getpwnam(value))) {
581 printf("bad user name \"%s\"\n", value);
582 exit(EX_USAGE);
584 snprintf(user, sizeof(user), "%u", pw->pw_uid);
585 } else {
586 strlcpy(user,value,sizeof(user));
589 goto nocopy;
590 } else if (strncmp(data, "gid", 3) == 0) {
591 if (value && *value) {
592 got_gid = 1;
593 if (!isdigit(*value)) {
594 struct group *gr;
596 if (!(gr = getgrnam(value))) {
597 printf("bad group name \"%s\"\n", value);
598 exit(EX_USAGE);
600 snprintf(group, sizeof(group), "%u", gr->gr_gid);
601 } else {
602 strlcpy(group,value,sizeof(group));
605 goto nocopy;
606 /* fmask and dmask synonyms for people used to smbfs syntax */
607 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
608 if (!value || !*value) {
609 printf ("Option '%s' requires a numerical argument\n", data);
610 SAFE_FREE(out);
611 return 1;
614 if (value[0] != '0') {
615 printf ("WARNING: '%s' not expressed in octal.\n", data);
618 if (strcmp (data, "fmask") == 0) {
619 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
620 data = "file_mode"; /* BB fix this */
622 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
623 if (!value || !*value) {
624 printf ("Option '%s' requires a numerical argument\n", data);
625 SAFE_FREE(out);
626 return 1;
629 if (value[0] != '0') {
630 printf ("WARNING: '%s' not expressed in octal.\n", data);
633 if (strcmp (data, "dmask") == 0) {
634 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
635 data = "dir_mode";
637 /* the following eight mount options should be
638 stripped out from what is passed into the kernel
639 since these eight options are best passed as the
640 mount flags rather than redundantly to the kernel
641 and could generate spurious warnings depending on the
642 level of the corresponding cifs vfs kernel code */
643 } else if (strncmp(data, "nosuid", 6) == 0) {
644 *filesys_flags |= MS_NOSUID;
645 } else if (strncmp(data, "suid", 4) == 0) {
646 *filesys_flags &= ~MS_NOSUID;
647 } else if (strncmp(data, "nodev", 5) == 0) {
648 *filesys_flags |= MS_NODEV;
649 } else if ((strncmp(data, "nobrl", 5) == 0) ||
650 (strncmp(data, "nolock", 6) == 0)) {
651 *filesys_flags &= ~MS_MANDLOCK;
652 } else if (strncmp(data, "dev", 3) == 0) {
653 *filesys_flags &= ~MS_NODEV;
654 } else if (strncmp(data, "noexec", 6) == 0) {
655 *filesys_flags |= MS_NOEXEC;
656 } else if (strncmp(data, "exec", 4) == 0) {
657 *filesys_flags &= ~MS_NOEXEC;
658 } else if (strncmp(data, "guest", 5) == 0) {
659 got_password=1;
660 } else if (strncmp(data, "ro", 2) == 0) {
661 *filesys_flags |= MS_RDONLY;
662 } else if (strncmp(data, "rw", 2) == 0) {
663 *filesys_flags &= ~MS_RDONLY;
664 } else if (strncmp(data, "remount", 7) == 0) {
665 *filesys_flags |= MS_REMOUNT;
666 } /* else if (strnicmp(data, "port", 4) == 0) {
667 if (value && *value) {
668 vol->port =
669 simple_strtoul(value, &value, 0);
671 } else if (strnicmp(data, "rsize", 5) == 0) {
672 if (value && *value) {
673 vol->rsize =
674 simple_strtoul(value, &value, 0);
676 } else if (strnicmp(data, "wsize", 5) == 0) {
677 if (value && *value) {
678 vol->wsize =
679 simple_strtoul(value, &value, 0);
681 } else if (strnicmp(data, "version", 3) == 0) {
682 } else {
683 printf("CIFS: Unknown mount option %s\n",data);
684 } */ /* nothing to do on those four mount options above.
685 Just pass to kernel and ignore them here */
687 /* Copy (possibly modified) option to out */
688 word_len = strlen(data);
689 if (value)
690 word_len += 1 + strlen(value);
692 out = (char *)realloc(out, out_len + word_len + 2);
693 if (out == NULL) {
694 perror("malloc");
695 exit(EX_SYSERR);
698 if (out_len) {
699 strlcat(out, ",", out_len + word_len + 2);
700 out_len++;
703 if (value)
704 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
705 else
706 snprintf(out + out_len, word_len + 1, "%s", data);
707 out_len = strlen(out);
709 nocopy:
710 data = next_keyword;
713 /* special-case the uid and gid */
714 if (got_uid) {
715 word_len = strlen(user);
717 out = (char *)realloc(out, out_len + word_len + 6);
718 if (out == NULL) {
719 perror("malloc");
720 exit(EX_SYSERR);
723 if (out_len) {
724 strlcat(out, ",", out_len + word_len + 6);
725 out_len++;
727 snprintf(out + out_len, word_len + 5, "uid=%s", user);
728 out_len = strlen(out);
730 if (got_gid) {
731 word_len = strlen(group);
733 out = (char *)realloc(out, out_len + 1 + word_len + 6);
734 if (out == NULL) {
735 perror("malloc");
736 exit(EX_SYSERR);
739 if (out_len) {
740 strlcat(out, ",", out_len + word_len + 6);
741 out_len++;
743 snprintf(out + out_len, word_len + 5, "gid=%s", group);
744 out_len = strlen(out);
747 SAFE_FREE(*optionsp);
748 *optionsp = out;
749 return 0;
752 /* replace all (one or more) commas with double commas */
753 static void check_for_comma(char ** ppasswrd)
755 char *new_pass_buf;
756 char *pass;
757 int i,j;
758 int number_of_commas = 0;
759 int len;
761 if(ppasswrd == NULL)
762 return;
763 else
764 (pass = *ppasswrd);
766 len = strlen(pass);
768 for(i=0;i<len;i++) {
769 if(pass[i] == ',')
770 number_of_commas++;
773 if(number_of_commas == 0)
774 return;
775 if(number_of_commas > MOUNT_PASSWD_SIZE) {
776 /* would otherwise overflow the mount options buffer */
777 printf("\nInvalid password. Password contains too many commas.\n");
778 return;
781 new_pass_buf = (char *)malloc(len+number_of_commas+1);
782 if(new_pass_buf == NULL)
783 return;
785 for(i=0,j=0;i<len;i++,j++) {
786 new_pass_buf[j] = pass[i];
787 if(pass[i] == ',') {
788 j++;
789 new_pass_buf[j] = pass[i];
792 new_pass_buf[len+number_of_commas] = 0;
794 SAFE_FREE(*ppasswrd);
795 *ppasswrd = new_pass_buf;
797 return;
800 /* Usernames can not have backslash in them and we use
801 [BB check if usernames can have forward slash in them BB]
802 backslash as domain\user separator character
804 static char * check_for_domain(char **ppuser)
806 char * original_string;
807 char * usernm;
808 char * domainnm;
809 int original_len;
810 int len;
811 int i;
813 if(ppuser == NULL)
814 return NULL;
816 original_string = *ppuser;
818 if (original_string == NULL)
819 return NULL;
821 original_len = strlen(original_string);
823 usernm = strchr(*ppuser,'/');
824 if (usernm == NULL) {
825 usernm = strchr(*ppuser,'\\');
826 if (usernm == NULL)
827 return NULL;
830 if(got_domain) {
831 printf("Domain name specified twice. Username probably malformed\n");
832 return NULL;
835 usernm[0] = 0;
836 domainnm = *ppuser;
837 if (domainnm[0] != 0) {
838 got_domain = 1;
839 } else {
840 printf("null domain\n");
842 len = strlen(domainnm);
843 /* reset domainm to new buffer, and copy
844 domain name into it */
845 domainnm = (char *)malloc(len+1);
846 if(domainnm == NULL)
847 return NULL;
849 strlcpy(domainnm,*ppuser,len+1);
851 /* move_string(*ppuser, usernm+1) */
852 len = strlen(usernm+1);
854 if(len >= original_len) {
855 /* should not happen */
856 return domainnm;
859 for(i=0;i<original_len;i++) {
860 if(i<len)
861 original_string[i] = usernm[i+1];
862 else /* stuff with commas to remove last parm */
863 original_string[i] = ',';
866 /* BB add check for more than one slash?
867 strchr(*ppuser,'/');
868 strchr(*ppuser,'\\')
871 return domainnm;
874 /* replace all occurances of "from" in a string with "to" */
875 static void replace_char(char *string, char from, char to, int maxlen)
877 char *lastchar = string + maxlen;
878 while (string) {
879 string = strchr(string, from);
880 if (string) {
881 *string = to;
882 if (string >= lastchar)
883 return;
888 /* Note that caller frees the returned buffer if necessary */
889 static char * parse_server(char ** punc_name)
891 char * unc_name = *punc_name;
892 int length = strnlen(unc_name, MAX_UNC_LEN);
893 char * share;
894 char * ipaddress_string = NULL;
895 struct hostent * host_entry = NULL;
896 struct in_addr server_ipaddr;
898 if(length > (MAX_UNC_LEN - 1)) {
899 printf("mount error: UNC name too long");
900 return NULL;
902 if (strncasecmp("cifs://",unc_name,7) == 0)
903 return parse_cifs_url(unc_name+7);
904 if (strncasecmp("smb://",unc_name,6) == 0) {
905 return parse_cifs_url(unc_name+6);
908 if(length < 3) {
909 /* BB add code to find DFS root here */
910 printf("\nMounting the DFS root for domain not implemented yet\n");
911 return NULL;
912 } else {
913 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
914 /* check for nfs syntax ie server:share */
915 share = strchr(unc_name,':');
916 if(share) {
917 *punc_name = (char *)malloc(length+3);
918 if(*punc_name == NULL) {
919 /* put the original string back if
920 no memory left */
921 *punc_name = unc_name;
922 return NULL;
924 *share = '/';
925 strlcpy((*punc_name)+2,unc_name,length+1);
926 SAFE_FREE(unc_name);
927 unc_name = *punc_name;
928 unc_name[length+2] = 0;
929 goto continue_unc_parsing;
930 } else {
931 printf("mount error: improperly formatted UNC name.");
932 printf(" %s does not begin with \\\\ or //\n",unc_name);
933 return NULL;
935 } else {
936 continue_unc_parsing:
937 unc_name[0] = '/';
938 unc_name[1] = '/';
939 unc_name += 2;
941 /* allow for either delimiter between host and sharename */
942 if ((share = strpbrk(unc_name, "/\\"))) {
943 *share = 0; /* temporarily terminate the string */
944 share += 1;
945 if(got_ip == 0) {
946 host_entry = gethostbyname(unc_name);
948 *(share - 1) = '/'; /* put delimiter back */
950 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
951 if ((prefixpath = strpbrk(share, "/\\"))) {
952 *prefixpath = 0; /* permanently terminate the string */
953 if (!strlen(++prefixpath))
954 prefixpath = NULL; /* this needs to be done explicitly */
956 if(got_ip) {
957 if(verboseflag)
958 printf("ip address specified explicitly\n");
959 return NULL;
961 if(host_entry == NULL) {
962 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
963 return NULL;
964 } else {
965 /* BB should we pass an alternate version of the share name as Unicode */
966 /* BB what about ipv6? BB */
967 /* BB add retries with alternate servers in list */
969 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
971 ipaddress_string = inet_ntoa(server_ipaddr);
972 if(ipaddress_string == NULL) {
973 printf("mount error: could not get valid ip address for target server\n");
974 return NULL;
976 return ipaddress_string;
978 } else {
979 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
980 printf("Mounting the DFS root for a particular server not implemented yet\n");
981 return NULL;
987 static struct option longopts[] = {
988 { "all", 0, NULL, 'a' },
989 { "help",0, NULL, 'h' },
990 { "move",0, NULL, 'm' },
991 { "bind",0, NULL, 'b' },
992 { "read-only", 0, NULL, 'r' },
993 { "ro", 0, NULL, 'r' },
994 { "verbose", 0, NULL, 'v' },
995 { "version", 0, NULL, 'V' },
996 { "read-write", 0, NULL, 'w' },
997 { "rw", 0, NULL, 'w' },
998 { "options", 1, NULL, 'o' },
999 { "type", 1, NULL, 't' },
1000 { "rsize",1, NULL, 'R' },
1001 { "wsize",1, NULL, 'W' },
1002 { "uid", 1, NULL, '1'},
1003 { "gid", 1, NULL, '2'},
1004 { "user",1,NULL,'u'},
1005 { "username",1,NULL,'u'},
1006 { "dom",1,NULL,'d'},
1007 { "domain",1,NULL,'d'},
1008 { "password",1,NULL,'p'},
1009 { "pass",1,NULL,'p'},
1010 { "credentials",1,NULL,'c'},
1011 { "port",1,NULL,'P'},
1012 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1013 { NULL, 0, NULL, 0 }
1016 /* convert a string to uppercase. return false if the string
1017 * wasn't ASCII. Return success on a NULL ptr */
1018 static int
1019 uppercase_string(char *string)
1021 if (!string)
1022 return 1;
1024 while (*string) {
1025 /* check for unicode */
1026 if ((unsigned char) string[0] & 0x80)
1027 return 0;
1028 *string = toupper((unsigned char) *string);
1029 string++;
1032 return 1;
1035 int main(int argc, char ** argv)
1037 int c;
1038 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1039 char * orgoptions = NULL;
1040 char * share_name = NULL;
1041 char * ipaddr = NULL;
1042 char * uuid = NULL;
1043 char * mountpoint = NULL;
1044 char * options = NULL;
1045 char * resolved_path = NULL;
1046 char * temp;
1047 char * dev_name;
1048 int rc;
1049 int rsize = 0;
1050 int wsize = 0;
1051 int nomtab = 0;
1052 int uid = 0;
1053 int gid = 0;
1054 int optlen = 0;
1055 int orgoptlen = 0;
1056 size_t options_size = 0;
1057 int retry = 0; /* set when we have to retry mount with uppercase */
1058 struct stat statbuf;
1059 struct utsname sysinfo;
1060 struct mntent mountent;
1061 FILE * pmntfile;
1063 /* setlocale(LC_ALL, "");
1064 bindtextdomain(PACKAGE, LOCALEDIR);
1065 textdomain(PACKAGE); */
1067 if(argc && argv) {
1068 thisprogram = argv[0];
1069 } else {
1070 mount_cifs_usage();
1071 exit(EX_USAGE);
1074 if(thisprogram == NULL)
1075 thisprogram = "mount.cifs";
1077 uname(&sysinfo);
1078 /* BB add workstation name and domain and pass down */
1080 /* #ifdef _GNU_SOURCE
1081 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1082 #endif */
1083 if(argc > 2) {
1084 dev_name = argv[1];
1085 share_name = strndup(argv[1], MAX_UNC_LEN);
1086 if (share_name == NULL) {
1087 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1088 exit(EX_SYSERR);
1090 mountpoint = argv[2];
1091 } else {
1092 mount_cifs_usage();
1093 exit(EX_USAGE);
1096 /* add sharename in opts string as unc= parm */
1098 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1099 longopts, NULL)) != -1) {
1100 switch (c) {
1101 /* No code to do the following options yet */
1102 /* case 'l':
1103 list_with_volumelabel = 1;
1104 break;
1105 case 'L':
1106 volumelabel = optarg;
1107 break; */
1108 /* case 'a':
1109 ++mount_all;
1110 break; */
1112 case '?':
1113 case 'h': /* help */
1114 mount_cifs_usage ();
1115 exit(EX_USAGE);
1116 case 'n':
1117 ++nomtab;
1118 break;
1119 case 'b':
1120 #ifdef MS_BIND
1121 flags |= MS_BIND;
1122 #else
1123 fprintf(stderr,
1124 "option 'b' (MS_BIND) not supported\n");
1125 #endif
1126 break;
1127 case 'm':
1128 #ifdef MS_MOVE
1129 flags |= MS_MOVE;
1130 #else
1131 fprintf(stderr,
1132 "option 'm' (MS_MOVE) not supported\n");
1133 #endif
1134 break;
1135 case 'o':
1136 orgoptions = strdup(optarg);
1137 break;
1138 case 'r': /* mount readonly */
1139 flags |= MS_RDONLY;
1140 break;
1141 case 'U':
1142 uuid = optarg;
1143 break;
1144 case 'v':
1145 ++verboseflag;
1146 break;
1147 case 'V':
1148 printf ("mount.cifs version: %s.%s%s\n",
1149 MOUNT_CIFS_VERSION_MAJOR,
1150 MOUNT_CIFS_VERSION_MINOR,
1151 MOUNT_CIFS_VENDOR_SUFFIX);
1152 exit (0);
1153 case 'w':
1154 flags &= ~MS_RDONLY;
1155 break;
1156 case 'R':
1157 rsize = atoi(optarg) ;
1158 break;
1159 case 'W':
1160 wsize = atoi(optarg);
1161 break;
1162 case '1':
1163 if (isdigit(*optarg)) {
1164 char *ep;
1166 uid = strtoul(optarg, &ep, 10);
1167 if (*ep) {
1168 printf("bad uid value \"%s\"\n", optarg);
1169 exit(EX_USAGE);
1171 } else {
1172 struct passwd *pw;
1174 if (!(pw = getpwnam(optarg))) {
1175 printf("bad user name \"%s\"\n", optarg);
1176 exit(EX_USAGE);
1178 uid = pw->pw_uid;
1179 endpwent();
1181 break;
1182 case '2':
1183 if (isdigit(*optarg)) {
1184 char *ep;
1186 gid = strtoul(optarg, &ep, 10);
1187 if (*ep) {
1188 printf("bad gid value \"%s\"\n", optarg);
1189 exit(EX_USAGE);
1191 } else {
1192 struct group *gr;
1194 if (!(gr = getgrnam(optarg))) {
1195 printf("bad user name \"%s\"\n", optarg);
1196 exit(EX_USAGE);
1198 gid = gr->gr_gid;
1199 endpwent();
1201 break;
1202 case 'u':
1203 got_user = 1;
1204 user_name = optarg;
1205 break;
1206 case 'd':
1207 domain_name = optarg; /* BB fix this - currently ignored */
1208 got_domain = 1;
1209 break;
1210 case 'p':
1211 if(mountpassword == NULL)
1212 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1213 if(mountpassword) {
1214 got_password = 1;
1215 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1217 break;
1218 case 'S':
1219 get_password_from_file(0 /* stdin */,NULL);
1220 break;
1221 case 't':
1222 break;
1223 default:
1224 printf("unknown mount option %c\n",c);
1225 mount_cifs_usage();
1226 exit(EX_USAGE);
1230 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1231 mount_cifs_usage();
1232 exit(EX_USAGE);
1235 if (getenv("PASSWD")) {
1236 if(mountpassword == NULL)
1237 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1238 if(mountpassword) {
1239 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1240 got_password = 1;
1242 } else if (getenv("PASSWD_FD")) {
1243 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1244 } else if (getenv("PASSWD_FILE")) {
1245 get_password_from_file(0, getenv("PASSWD_FILE"));
1248 if (orgoptions && parse_options(&orgoptions, &flags)) {
1249 rc = EX_USAGE;
1250 goto mount_exit;
1252 ipaddr = parse_server(&share_name);
1253 if((ipaddr == NULL) && (got_ip == 0)) {
1254 printf("No ip address specified and hostname not found\n");
1255 rc = EX_USAGE;
1256 goto mount_exit;
1259 /* BB save off path and pop after mount returns? */
1260 resolved_path = (char *)malloc(PATH_MAX+1);
1261 if(resolved_path) {
1262 /* Note that if we can not canonicalize the name, we get
1263 another chance to see if it is valid when we chdir to it */
1264 if (realpath(mountpoint, resolved_path)) {
1265 mountpoint = resolved_path;
1268 if(chdir(mountpoint)) {
1269 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1270 rc = EX_USAGE;
1271 goto mount_exit;
1274 if(stat (".", &statbuf)) {
1275 printf("mount error: mount point %s does not exist\n",mountpoint);
1276 rc = EX_USAGE;
1277 goto mount_exit;
1280 if (S_ISDIR(statbuf.st_mode) == 0) {
1281 printf("mount error: mount point %s is not a directory\n",mountpoint);
1282 rc = EX_USAGE;
1283 goto mount_exit;
1286 if((getuid() != 0) && (geteuid() == 0)) {
1287 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1288 #ifndef CIFS_ALLOW_USR_SUID
1289 /* Do not allow user mounts to control suid flag
1290 for mount unless explicitly built that way */
1291 flags |= MS_NOSUID | MS_NODEV;
1292 #endif
1293 } else {
1294 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1295 exit(EX_USAGE);
1299 if(got_user == 0) {
1300 user_name = getusername();
1301 got_user = 1;
1304 if(got_password == 0) {
1305 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1306 no good replacement yet. */
1307 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1308 if (!tmp_pass || !mountpassword) {
1309 printf("Password not entered, exiting\n");
1310 exit(EX_USAGE);
1312 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1313 got_password = 1;
1315 /* FIXME launch daemon (handles dfs name resolution and credential change)
1316 remember to clear parms and overwrite password field before launching */
1317 mount_retry:
1318 if(orgoptions) {
1319 optlen = strlen(orgoptions);
1320 orgoptlen = optlen;
1321 } else
1322 optlen = 0;
1323 if(share_name)
1324 optlen += strlen(share_name) + 4;
1325 else {
1326 printf("No server share name specified\n");
1327 printf("\nMounting the DFS root for server not implemented yet\n");
1328 exit(EX_USAGE);
1330 if(user_name)
1331 optlen += strlen(user_name) + 6;
1332 if(ipaddr)
1333 optlen += strlen(ipaddr) + 4;
1334 if(mountpassword)
1335 optlen += strlen(mountpassword) + 6;
1336 SAFE_FREE(options);
1337 options_size = optlen + 10 + DOMAIN_SIZE;
1338 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 */);
1340 if(options == NULL) {
1341 printf("Could not allocate memory for mount options\n");
1342 exit(EX_SYSERR);
1345 options[0] = 0;
1346 strlcpy(options,"unc=",options_size);
1347 strlcat(options,share_name,options_size);
1348 /* scan backwards and reverse direction of slash */
1349 temp = strrchr(options, '/');
1350 if(temp > options + 6)
1351 *temp = '\\';
1352 if(ipaddr) {
1353 strlcat(options,",ip=",options_size);
1354 strlcat(options,ipaddr,options_size);
1357 if(user_name) {
1358 /* check for syntax like user=domain\user */
1359 if(got_domain == 0)
1360 domain_name = check_for_domain(&user_name);
1361 strlcat(options,",user=",options_size);
1362 strlcat(options,user_name,options_size);
1364 if(retry == 0) {
1365 if(domain_name) {
1366 /* extra length accounted for in option string above */
1367 strlcat(options,",domain=",options_size);
1368 strlcat(options,domain_name,options_size);
1371 if(mountpassword) {
1372 /* Commas have to be doubled, or else they will
1373 look like the parameter separator */
1374 /* if(sep is not set)*/
1375 if(retry == 0)
1376 check_for_comma(&mountpassword);
1377 strlcat(options,",pass=",options_size);
1378 strlcat(options,mountpassword,options_size);
1381 strlcat(options,",ver=",options_size);
1382 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1384 if(orgoptions) {
1385 strlcat(options,",",options_size);
1386 strlcat(options,orgoptions,options_size);
1388 if(prefixpath) {
1389 strlcat(options,",prefixpath=",options_size);
1390 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1392 if(verboseflag)
1393 printf("\nmount.cifs kernel mount options %s \n",options);
1395 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1396 replace_char(dev_name, '\\', '/', strlen(share_name));
1398 if(mount(dev_name, mountpoint, "cifs", flags, options)) {
1399 /* remember to kill daemon on error */
1400 switch (errno) {
1401 case 0:
1402 printf("mount failed but no error number set\n");
1403 break;
1404 case ENODEV:
1405 printf("mount error: cifs filesystem not supported by the system\n");
1406 break;
1407 case ENXIO:
1408 if(retry == 0) {
1409 retry = 1;
1410 if (uppercase_string(dev_name) &&
1411 uppercase_string(share_name) &&
1412 uppercase_string(prefixpath)) {
1413 printf("retrying with upper case share name\n");
1414 goto mount_retry;
1417 default:
1418 printf("mount error %d = %s\n",errno,strerror(errno));
1420 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1421 rc = EX_FAIL;
1422 } else {
1423 pmntfile = setmntent(MOUNTED, "a+");
1424 if(pmntfile) {
1425 mountent.mnt_fsname = dev_name;
1426 mountent.mnt_dir = mountpoint;
1427 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1428 mountent.mnt_opts = (char *)malloc(220);
1429 if(mountent.mnt_opts) {
1430 char * mount_user = getusername();
1431 memset(mountent.mnt_opts,0,200);
1432 if(flags & MS_RDONLY)
1433 strlcat(mountent.mnt_opts,"ro",220);
1434 else
1435 strlcat(mountent.mnt_opts,"rw",220);
1436 if(flags & MS_MANDLOCK)
1437 strlcat(mountent.mnt_opts,",mand",220);
1438 if(flags & MS_NOEXEC)
1439 strlcat(mountent.mnt_opts,",noexec",220);
1440 if(flags & MS_NOSUID)
1441 strlcat(mountent.mnt_opts,",nosuid",220);
1442 if(flags & MS_NODEV)
1443 strlcat(mountent.mnt_opts,",nodev",220);
1444 if(flags & MS_SYNCHRONOUS)
1445 strlcat(mountent.mnt_opts,",synch",220);
1446 if(mount_user) {
1447 if(getuid() != 0) {
1448 strlcat(mountent.mnt_opts,",user=",220);
1449 strlcat(mountent.mnt_opts,mount_user,220);
1451 /* free(mount_user); do not free static mem */
1454 mountent.mnt_freq = 0;
1455 mountent.mnt_passno = 0;
1456 rc = addmntent(pmntfile,&mountent);
1457 endmntent(pmntfile);
1458 SAFE_FREE(mountent.mnt_opts);
1459 if (rc)
1460 rc = EX_FILEIO;
1461 } else {
1462 printf("could not update mount table\n");
1463 rc = EX_FILEIO;
1466 mount_exit:
1467 if(mountpassword) {
1468 int len = strlen(mountpassword);
1469 memset(mountpassword,0,len);
1470 SAFE_FREE(mountpassword);
1473 SAFE_FREE(options);
1474 SAFE_FREE(orgoptions);
1475 SAFE_FREE(resolved_path);
1476 SAFE_FREE(share_name);
1477 exit(rc);