Tidyup to ensure '\n' is treated identically in all
[Samba.git] / source / client / mount.cifs.c
blobd1c7a69c61f8c18633e4a39745120677f3203d36
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 #ifndef MS_MOVE
60 #define MS_MOVE 8192
61 #endif
63 #ifndef MS_BIND
64 #define MS_BIND 4096
65 #endif
67 #define MAX_UNC_LEN 1024
69 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
71 #ifndef SAFE_FREE
72 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
73 #endif
75 #define MOUNT_PASSWD_SIZE 64
76 #define DOMAIN_SIZE 64
78 const char *thisprogram;
79 int verboseflag = 0;
80 static int got_password = 0;
81 static int got_user = 0;
82 static int got_domain = 0;
83 static int got_ip = 0;
84 static int got_unc = 0;
85 static int got_uid = 0;
86 static int got_gid = 0;
87 static char * user_name = NULL;
88 static char * mountpassword = NULL;
89 char * domain_name = NULL;
90 char * prefixpath = NULL;
92 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
93 * don't link to libreplace so need them here. */
95 /* like strncpy but does not 0 fill the buffer and always null
96 * terminates. bufsize is the size of the destination buffer */
97 size_t strlcpy(char *d, const char *s, size_t bufsize)
99 size_t len = strlen(s);
100 size_t ret = len;
101 if (bufsize <= 0) return 0;
102 if (len >= bufsize) len = bufsize-1;
103 memcpy(d, s, len);
104 d[len] = 0;
105 return ret;
108 /* like strncat but does not 0 fill the buffer and always null
109 * terminates. bufsize is the length of the buffer, which should
110 * be one more than the maximum resulting string length */
111 size_t strlcat(char *d, const char *s, size_t bufsize)
113 size_t len1 = strlen(d);
114 size_t len2 = strlen(s);
115 size_t ret = len1 + len2;
117 if (len1+len2 >= bufsize) {
118 len2 = bufsize - (len1+1);
120 if (len2 > 0) {
121 memcpy(d+len1, s, len2);
122 d[len1+len2] = 0;
124 return ret;
127 /* BB finish BB
129 cifs_umount
130 open nofollow - avoid symlink exposure?
131 get owner of dir see if matches self or if root
132 call system(umount argv) etc.
134 BB end finish BB */
136 static char * check_for_domain(char **);
139 static void mount_cifs_usage(void)
141 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
142 printf("\nMount the remote target, specified as a UNC name,");
143 printf(" to a local directory.\n\nOptions:\n");
144 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
145 printf("\nLess commonly used options:");
146 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
147 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
148 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
149 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
150 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
151 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
152 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
153 printf("\n\nRarely used options:");
154 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
155 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
156 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
157 printf("\n\tin6_addr");
158 printf("\n\nOptions are described in more detail in the manual page");
159 printf("\n\tman 8 mount.cifs\n");
160 printf("\nTo display the version number of the mount helper:");
161 printf("\n\t%s -V\n",thisprogram);
163 SAFE_FREE(mountpassword);
164 exit(1);
167 /* caller frees username if necessary */
168 static char * getusername(void) {
169 char *username = NULL;
170 struct passwd *password = getpwuid(getuid());
172 if (password) {
173 username = password->pw_name;
175 return username;
178 static char * parse_cifs_url(char * unc_name)
180 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
181 return NULL;
184 static int open_cred_file(char * file_name)
186 char * line_buf;
187 char * temp_val;
188 FILE * fs;
189 int i, length;
190 fs = fopen(file_name,"r");
191 if(fs == NULL)
192 return errno;
193 line_buf = (char *)malloc(4096);
194 if(line_buf == NULL) {
195 fclose(fs);
196 return -ENOMEM;
199 while(fgets(line_buf,4096,fs)) {
200 /* parse line from credential file */
202 /* eat leading white space */
203 for(i=0;i<4086;i++) {
204 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
205 break;
206 /* if whitespace - skip past it */
208 if (strncasecmp("username",line_buf+i,8) == 0) {
209 temp_val = strchr(line_buf + i,'=');
210 if(temp_val) {
211 /* go past equals sign */
212 temp_val++;
213 for(length = 0;length<4087;length++) {
214 if ((temp_val[length] == '\n')
215 || (temp_val[length] == '\0')) {
216 temp_val[length] = '\0';
217 break;
220 if(length > 4086) {
221 printf("mount.cifs failed due to malformed username in credentials file");
222 memset(line_buf,0,4096);
223 exit(1);
224 } else {
225 got_user = 1;
226 user_name = (char *)calloc(1 + length,1);
227 /* BB adding free of user_name string before exit,
228 not really necessary but would be cleaner */
229 strlcpy(user_name,temp_val, length+1);
232 } else if (strncasecmp("password",line_buf+i,8) == 0) {
233 temp_val = strchr(line_buf+i,'=');
234 if(temp_val) {
235 /* go past equals sign */
236 temp_val++;
237 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
238 if ((temp_val[length] == '\n')
239 || (temp_val[length] == '\0')) {
240 temp_val[length] = '\0';
241 break;
244 if(length > MOUNT_PASSWD_SIZE) {
245 printf("mount.cifs failed: password in credentials file too long\n");
246 memset(line_buf,0, 4096);
247 exit(1);
248 } else {
249 if(mountpassword == NULL) {
250 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
251 } else
252 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
253 if(mountpassword) {
254 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
255 got_password = 1;
259 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
260 temp_val = strchr(line_buf+i,'=');
261 if(temp_val) {
262 /* go past equals sign */
263 temp_val++;
264 if(verboseflag)
265 printf("\nDomain %s\n",temp_val);
266 for(length = 0;length<DOMAIN_SIZE+1;length++) {
267 if ((temp_val[length] == '\n')
268 || (temp_val[length] == '\0')) {
269 temp_val[length] = '\0';
270 break;
273 if(length > DOMAIN_SIZE) {
274 printf("mount.cifs failed: domain in credentials file too long\n");
275 exit(1);
276 } else {
277 if(domain_name == NULL) {
278 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
279 } else
280 memset(domain_name,0,DOMAIN_SIZE);
281 if(domain_name) {
282 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
283 got_domain = 1;
290 fclose(fs);
291 SAFE_FREE(line_buf);
292 return 0;
295 static int get_password_from_file(int file_descript, char * filename)
297 int rc = 0;
298 int i;
299 char c;
301 if(mountpassword == NULL)
302 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
303 else
304 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
306 if (mountpassword == NULL) {
307 printf("malloc failed\n");
308 exit(1);
311 if(filename != NULL) {
312 file_descript = open(filename, O_RDONLY);
313 if(file_descript < 0) {
314 printf("mount.cifs failed. %s attempting to open password file %s\n",
315 strerror(errno),filename);
316 exit(1);
319 /* else file already open and fd provided */
321 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
322 rc = read(file_descript,&c,1);
323 if(rc < 0) {
324 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
325 if(filename != NULL)
326 close(file_descript);
327 exit(1);
328 } else if(rc == 0) {
329 if(mountpassword[0] == 0) {
330 if(verboseflag)
331 printf("\nWarning: null password used since cifs password file empty");
333 break;
334 } else /* read valid character */ {
335 if((c == 0) || (c == '\n')) {
336 mountpassword[i] = '\0';
337 break;
338 } else
339 mountpassword[i] = c;
342 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
343 printf("\nWarning: password longer than %d characters specified in cifs password file",
344 MOUNT_PASSWD_SIZE);
346 got_password = 1;
347 if(filename != NULL) {
348 close(file_descript);
351 return rc;
354 static int parse_options(char ** optionsp, int * filesys_flags)
356 const char * data;
357 char * percent_char = NULL;
358 char * value = NULL;
359 char * next_keyword = NULL;
360 char * out = NULL;
361 int out_len = 0;
362 int word_len;
363 int rc = 0;
364 char user[32];
365 char group[32];
367 if (!optionsp || !*optionsp)
368 return 1;
369 data = *optionsp;
371 if(verboseflag)
372 printf("parsing options: %s\n", data);
374 /* BB fixme check for separator override BB */
376 if (getuid()) {
377 got_uid = 1;
378 snprintf(user,sizeof(user),"%u",getuid());
379 got_gid = 1;
380 snprintf(group,sizeof(group),"%u",getgid());
383 /* while ((data = strsep(&options, ",")) != NULL) { */
384 while(data != NULL) {
385 /* check if ends with trailing comma */
386 if(*data == 0)
387 break;
389 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
390 /* data = next keyword */
391 /* value = next value ie stuff after equal sign */
393 next_keyword = strchr(data,','); /* BB handle sep= */
395 /* temporarily null terminate end of keyword=value pair */
396 if(next_keyword)
397 *next_keyword++ = 0;
399 /* temporarily null terminate keyword to make keyword and value distinct */
400 if ((value = strchr(data, '=')) != NULL) {
401 *value = '\0';
402 value++;
405 if (strncmp(data, "users",5) == 0) {
406 if(!value || !*value) {
407 goto nocopy;
409 } else if (strncmp(data, "user_xattr",10) == 0) {
410 /* do nothing - need to skip so not parsed as user name */
411 } else if (strncmp(data, "user", 4) == 0) {
413 if (!value || !*value) {
414 if(data[4] == '\0') {
415 if(verboseflag)
416 printf("\nskipping empty user mount parameter\n");
417 /* remove the parm since it would otherwise be confusing
418 to the kernel code which would think it was a real username */
419 goto nocopy;
420 } else {
421 printf("username specified with no parameter\n");
422 return 1; /* needs_arg; */
424 } else {
425 if (strnlen(value, 260) < 260) {
426 got_user=1;
427 percent_char = strchr(value,'%');
428 if(percent_char) {
429 *percent_char = ',';
430 if(mountpassword == NULL)
431 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
432 if(mountpassword) {
433 if(got_password)
434 printf("\nmount.cifs warning - password specified twice\n");
435 got_password = 1;
436 percent_char++;
437 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
438 /* remove password from username */
439 while(*percent_char != 0) {
440 *percent_char = ',';
441 percent_char++;
445 /* this is only case in which the user
446 name buf is not malloc - so we have to
447 check for domain name embedded within
448 the user name here since the later
449 call to check_for_domain will not be
450 invoked */
451 domain_name = check_for_domain(&value);
452 } else {
453 printf("username too long\n");
454 return 1;
457 } else if (strncmp(data, "pass", 4) == 0) {
458 if (!value || !*value) {
459 if(got_password) {
460 printf("\npassword specified twice, ignoring second\n");
461 } else
462 got_password = 1;
463 } else if (strnlen(value, 17) < 17) {
464 if(got_password)
465 printf("\nmount.cifs warning - password specified twice\n");
466 got_password = 1;
467 } else {
468 printf("password too long\n");
469 return 1;
471 } else if (strncmp(data, "sec", 3) == 0) {
472 if (value) {
473 if (!strcmp(value, "none"))
474 got_password = 1;
476 } else if (strncmp(data, "ip", 2) == 0) {
477 if (!value || !*value) {
478 printf("target ip address argument missing");
479 } else if (strnlen(value, 35) < 35) {
480 if(verboseflag)
481 printf("ip address %s override specified\n",value);
482 got_ip = 1;
483 } else {
484 printf("ip address too long\n");
485 return 1;
487 } else if ((strncmp(data, "unc", 3) == 0)
488 || (strncmp(data, "target", 6) == 0)
489 || (strncmp(data, "path", 4) == 0)) {
490 if (!value || !*value) {
491 printf("invalid path to network resource\n");
492 return 1; /* needs_arg; */
493 } else if(strnlen(value,5) < 5) {
494 printf("UNC name too short");
497 if (strnlen(value, 300) < 300) {
498 got_unc = 1;
499 if (strncmp(value, "//", 2) == 0) {
500 if(got_unc)
501 printf("unc name specified twice, ignoring second\n");
502 else
503 got_unc = 1;
504 } else if (strncmp(value, "\\\\", 2) != 0) {
505 printf("UNC Path does not begin with // or \\\\ \n");
506 return 1;
507 } else {
508 if(got_unc)
509 printf("unc name specified twice, ignoring second\n");
510 else
511 got_unc = 1;
513 } else {
514 printf("CIFS: UNC name too long\n");
515 return 1;
517 } else if ((strncmp(data, "domain", 3) == 0)
518 || (strncmp(data, "workgroup", 5) == 0)) {
519 if (!value || !*value) {
520 printf("CIFS: invalid domain name\n");
521 return 1; /* needs_arg; */
523 if (strnlen(value, 65) < 65) {
524 got_domain = 1;
525 } else {
526 printf("domain name too long\n");
527 return 1;
529 } else if (strncmp(data, "cred", 4) == 0) {
530 if (value && *value) {
531 rc = open_cred_file(value);
532 if(rc) {
533 printf("error %d opening credential file %s\n",rc, value);
534 return 1;
536 } else {
537 printf("invalid credential file name specified\n");
538 return 1;
540 } else if (strncmp(data, "uid", 3) == 0) {
541 if (value && *value) {
542 got_uid = 1;
543 if (!isdigit(*value)) {
544 struct passwd *pw;
546 if (!(pw = getpwnam(value))) {
547 printf("bad user name \"%s\"\n", value);
548 exit(1);
550 snprintf(user, sizeof(user), "%u", pw->pw_uid);
551 } else {
552 strlcpy(user,value,sizeof(user));
555 goto nocopy;
556 } else if (strncmp(data, "gid", 3) == 0) {
557 if (value && *value) {
558 got_gid = 1;
559 if (!isdigit(*value)) {
560 struct group *gr;
562 if (!(gr = getgrnam(value))) {
563 printf("bad group name \"%s\"\n", value);
564 exit(1);
566 snprintf(group, sizeof(group), "%u", gr->gr_gid);
567 } else {
568 strlcpy(group,value,sizeof(group));
571 goto nocopy;
572 /* fmask and dmask synonyms for people used to smbfs syntax */
573 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
574 if (!value || !*value) {
575 printf ("Option '%s' requires a numerical argument\n", data);
576 return 1;
579 if (value[0] != '0') {
580 printf ("WARNING: '%s' not expressed in octal.\n", data);
583 if (strcmp (data, "fmask") == 0) {
584 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
585 data = "file_mode"; /* BB fix this */
587 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
588 if (!value || !*value) {
589 printf ("Option '%s' requires a numerical argument\n", data);
590 return 1;
593 if (value[0] != '0') {
594 printf ("WARNING: '%s' not expressed in octal.\n", data);
597 if (strcmp (data, "dmask") == 0) {
598 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
599 data = "dir_mode";
601 /* the following eight mount options should be
602 stripped out from what is passed into the kernel
603 since these eight options are best passed as the
604 mount flags rather than redundantly to the kernel
605 and could generate spurious warnings depending on the
606 level of the corresponding cifs vfs kernel code */
607 } else if (strncmp(data, "nosuid", 6) == 0) {
608 *filesys_flags |= MS_NOSUID;
609 } else if (strncmp(data, "suid", 4) == 0) {
610 *filesys_flags &= ~MS_NOSUID;
611 } else if (strncmp(data, "nodev", 5) == 0) {
612 *filesys_flags |= MS_NODEV;
613 } else if ((strncmp(data, "nobrl", 5) == 0) ||
614 (strncmp(data, "nolock", 6) == 0)) {
615 *filesys_flags &= ~MS_MANDLOCK;
616 } else if (strncmp(data, "dev", 3) == 0) {
617 *filesys_flags &= ~MS_NODEV;
618 } else if (strncmp(data, "noexec", 6) == 0) {
619 *filesys_flags |= MS_NOEXEC;
620 } else if (strncmp(data, "exec", 4) == 0) {
621 *filesys_flags &= ~MS_NOEXEC;
622 } else if (strncmp(data, "guest", 5) == 0) {
623 got_password=1;
624 } else if (strncmp(data, "ro", 2) == 0) {
625 *filesys_flags |= MS_RDONLY;
626 } else if (strncmp(data, "rw", 2) == 0) {
627 *filesys_flags &= ~MS_RDONLY;
628 } else if (strncmp(data, "remount", 7) == 0) {
629 *filesys_flags |= MS_REMOUNT;
630 } /* else if (strnicmp(data, "port", 4) == 0) {
631 if (value && *value) {
632 vol->port =
633 simple_strtoul(value, &value, 0);
635 } else if (strnicmp(data, "rsize", 5) == 0) {
636 if (value && *value) {
637 vol->rsize =
638 simple_strtoul(value, &value, 0);
640 } else if (strnicmp(data, "wsize", 5) == 0) {
641 if (value && *value) {
642 vol->wsize =
643 simple_strtoul(value, &value, 0);
645 } else if (strnicmp(data, "version", 3) == 0) {
646 } else {
647 printf("CIFS: Unknown mount option %s\n",data);
648 } */ /* nothing to do on those four mount options above.
649 Just pass to kernel and ignore them here */
651 /* Copy (possibly modified) option to out */
652 word_len = strlen(data);
653 if (value)
654 word_len += 1 + strlen(value);
656 out = (char *)realloc(out, out_len + word_len + 2);
657 if (out == NULL) {
658 perror("malloc");
659 exit(1);
662 if (out_len) {
663 strlcat(out, ",", out_len + word_len + 2);
664 out_len++;
667 if (value)
668 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
669 else
670 snprintf(out + out_len, word_len + 1, "%s", data);
671 out_len = strlen(out);
673 nocopy:
674 data = next_keyword;
677 /* special-case the uid and gid */
678 if (got_uid) {
679 word_len = strlen(user);
681 out = (char *)realloc(out, out_len + word_len + 6);
682 if (out == NULL) {
683 perror("malloc");
684 exit(1);
687 if (out_len) {
688 strlcat(out, ",", out_len + word_len + 6);
689 out_len++;
691 snprintf(out + out_len, word_len + 5, "uid=%s", user);
692 out_len = strlen(out);
694 if (got_gid) {
695 word_len = strlen(group);
697 out = (char *)realloc(out, out_len + 1 + word_len + 6);
698 if (out == NULL) {
699 perror("malloc");
700 exit(1);
703 if (out_len) {
704 strlcat(out, ",", out_len + word_len + 6);
705 out_len++;
707 snprintf(out + out_len, word_len + 5, "gid=%s", group);
708 out_len = strlen(out);
711 SAFE_FREE(*optionsp);
712 *optionsp = out;
713 return 0;
716 /* replace all (one or more) commas with double commas */
717 static void check_for_comma(char ** ppasswrd)
719 char *new_pass_buf;
720 char *pass;
721 int i,j;
722 int number_of_commas = 0;
723 int len;
725 if(ppasswrd == NULL)
726 return;
727 else
728 (pass = *ppasswrd);
730 len = strlen(pass);
732 for(i=0;i<len;i++) {
733 if(pass[i] == ',')
734 number_of_commas++;
737 if(number_of_commas == 0)
738 return;
739 if(number_of_commas > MOUNT_PASSWD_SIZE) {
740 /* would otherwise overflow the mount options buffer */
741 printf("\nInvalid password. Password contains too many commas.\n");
742 return;
745 new_pass_buf = (char *)malloc(len+number_of_commas+1);
746 if(new_pass_buf == NULL)
747 return;
749 for(i=0,j=0;i<len;i++,j++) {
750 new_pass_buf[j] = pass[i];
751 if(pass[i] == ',') {
752 j++;
753 new_pass_buf[j] = pass[i];
756 new_pass_buf[len+number_of_commas] = 0;
758 SAFE_FREE(*ppasswrd);
759 *ppasswrd = new_pass_buf;
761 return;
764 /* Usernames can not have backslash in them and we use
765 [BB check if usernames can have forward slash in them BB]
766 backslash as domain\user separator character
768 static char * check_for_domain(char **ppuser)
770 char * original_string;
771 char * usernm;
772 char * domainnm;
773 int original_len;
774 int len;
775 int i;
777 if(ppuser == NULL)
778 return NULL;
780 original_string = *ppuser;
782 if (original_string == NULL)
783 return NULL;
785 original_len = strlen(original_string);
787 usernm = strchr(*ppuser,'/');
788 if (usernm == NULL) {
789 usernm = strchr(*ppuser,'\\');
790 if (usernm == NULL)
791 return NULL;
794 if(got_domain) {
795 printf("Domain name specified twice. Username probably malformed\n");
796 return NULL;
799 usernm[0] = 0;
800 domainnm = *ppuser;
801 if (domainnm[0] != 0) {
802 got_domain = 1;
803 } else {
804 printf("null domain\n");
806 len = strlen(domainnm);
807 /* reset domainm to new buffer, and copy
808 domain name into it */
809 domainnm = (char *)malloc(len+1);
810 if(domainnm == NULL)
811 return NULL;
813 strlcpy(domainnm,*ppuser,len+1);
815 /* move_string(*ppuser, usernm+1) */
816 len = strlen(usernm+1);
818 if(len >= original_len) {
819 /* should not happen */
820 return domainnm;
823 for(i=0;i<original_len;i++) {
824 if(i<len)
825 original_string[i] = usernm[i+1];
826 else /* stuff with commas to remove last parm */
827 original_string[i] = ',';
830 /* BB add check for more than one slash?
831 strchr(*ppuser,'/');
832 strchr(*ppuser,'\\')
835 return domainnm;
838 /* replace all occurances of "from" in a string with "to" */
839 static void replace_char(char *string, char from, char to, int maxlen)
841 char *lastchar = string + maxlen;
842 while (string) {
843 string = strchr(string, from);
844 if (string) {
845 *string = to;
846 if (string >= lastchar)
847 return;
852 /* Note that caller frees the returned buffer if necessary */
853 static char * parse_server(char ** punc_name)
855 char * unc_name = *punc_name;
856 int length = strnlen(unc_name, MAX_UNC_LEN);
857 char * share;
858 char * ipaddress_string = NULL;
859 struct hostent * host_entry = NULL;
860 struct in_addr server_ipaddr;
862 if(length > (MAX_UNC_LEN - 1)) {
863 printf("mount error: UNC name too long");
864 return NULL;
866 if (strncasecmp("cifs://",unc_name,7) == 0)
867 return parse_cifs_url(unc_name+7);
868 if (strncasecmp("smb://",unc_name,6) == 0) {
869 return parse_cifs_url(unc_name+6);
872 if(length < 3) {
873 /* BB add code to find DFS root here */
874 printf("\nMounting the DFS root for domain not implemented yet\n");
875 return NULL;
876 } else {
877 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
878 /* check for nfs syntax ie server:share */
879 share = strchr(unc_name,':');
880 if(share) {
881 *punc_name = (char *)malloc(length+3);
882 if(*punc_name == NULL) {
883 /* put the original string back if
884 no memory left */
885 *punc_name = unc_name;
886 return NULL;
888 *share = '/';
889 strlcpy((*punc_name)+2,unc_name,length+1);
890 SAFE_FREE(unc_name);
891 unc_name = *punc_name;
892 unc_name[length+2] = 0;
893 goto continue_unc_parsing;
894 } else {
895 printf("mount error: improperly formatted UNC name.");
896 printf(" %s does not begin with \\\\ or //\n",unc_name);
897 return NULL;
899 } else {
900 continue_unc_parsing:
901 unc_name[0] = '/';
902 unc_name[1] = '/';
903 unc_name += 2;
905 /* allow for either delimiter between host and sharename */
906 if ((share = strpbrk(unc_name, "/\\"))) {
907 *share = 0; /* temporarily terminate the string */
908 share += 1;
909 if(got_ip == 0) {
910 host_entry = gethostbyname(unc_name);
912 *(share - 1) = '/'; /* put delimiter back */
914 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
915 if ((prefixpath = strpbrk(share, "/\\"))) {
916 *prefixpath = 0; /* permanently terminate the string */
917 if (!strlen(++prefixpath))
918 prefixpath = NULL; /* this needs to be done explicitly */
920 if(got_ip) {
921 if(verboseflag)
922 printf("ip address specified explicitly\n");
923 return NULL;
925 if(host_entry == NULL) {
926 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
927 return NULL;
928 } else {
929 /* BB should we pass an alternate version of the share name as Unicode */
930 /* BB what about ipv6? BB */
931 /* BB add retries with alternate servers in list */
933 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
935 ipaddress_string = inet_ntoa(server_ipaddr);
936 if(ipaddress_string == NULL) {
937 printf("mount error: could not get valid ip address for target server\n");
938 return NULL;
940 return ipaddress_string;
942 } else {
943 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
944 printf("Mounting the DFS root for a particular server not implemented yet\n");
945 return NULL;
951 static struct option longopts[] = {
952 { "all", 0, NULL, 'a' },
953 { "help",0, NULL, 'h' },
954 { "move",0, NULL, 'm' },
955 { "bind",0, NULL, 'b' },
956 { "read-only", 0, NULL, 'r' },
957 { "ro", 0, NULL, 'r' },
958 { "verbose", 0, NULL, 'v' },
959 { "version", 0, NULL, 'V' },
960 { "read-write", 0, NULL, 'w' },
961 { "rw", 0, NULL, 'w' },
962 { "options", 1, NULL, 'o' },
963 { "type", 1, NULL, 't' },
964 { "rsize",1, NULL, 'R' },
965 { "wsize",1, NULL, 'W' },
966 { "uid", 1, NULL, '1'},
967 { "gid", 1, NULL, '2'},
968 { "user",1,NULL,'u'},
969 { "username",1,NULL,'u'},
970 { "dom",1,NULL,'d'},
971 { "domain",1,NULL,'d'},
972 { "password",1,NULL,'p'},
973 { "pass",1,NULL,'p'},
974 { "credentials",1,NULL,'c'},
975 { "port",1,NULL,'P'},
976 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
977 { NULL, 0, NULL, 0 }
980 /* convert a string to uppercase. return false if the string
981 * wasn't ASCII or was a NULL ptr */
982 static int
983 uppercase_string(char *string)
985 if (!string)
986 return 0;
988 while (*string) {
989 /* check for unicode */
990 if ((unsigned char) string[0] & 0x80)
991 return 0;
992 *string = toupper((unsigned char) *string);
993 string++;
996 return 1;
999 int main(int argc, char ** argv)
1001 int c;
1002 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1003 char * orgoptions = NULL;
1004 char * share_name = NULL;
1005 char * ipaddr = NULL;
1006 char * uuid = NULL;
1007 char * mountpoint = NULL;
1008 char * options = NULL;
1009 char * resolved_path = NULL;
1010 char * temp;
1011 char * dev_name;
1012 int rc;
1013 int rsize = 0;
1014 int wsize = 0;
1015 int nomtab = 0;
1016 int uid = 0;
1017 int gid = 0;
1018 int optlen = 0;
1019 int orgoptlen = 0;
1020 size_t options_size = 0;
1021 int retry = 0; /* set when we have to retry mount with uppercase */
1022 struct stat statbuf;
1023 struct utsname sysinfo;
1024 struct mntent mountent;
1025 FILE * pmntfile;
1027 /* setlocale(LC_ALL, "");
1028 bindtextdomain(PACKAGE, LOCALEDIR);
1029 textdomain(PACKAGE); */
1031 if(argc && argv) {
1032 thisprogram = argv[0];
1033 } else {
1034 mount_cifs_usage();
1035 exit(1);
1038 if(thisprogram == NULL)
1039 thisprogram = "mount.cifs";
1041 uname(&sysinfo);
1042 /* BB add workstation name and domain and pass down */
1044 /* #ifdef _GNU_SOURCE
1045 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1046 #endif */
1047 if(argc > 2) {
1048 dev_name = argv[1];
1049 share_name = strndup(argv[1], MAX_UNC_LEN);
1050 if (share_name == NULL) {
1051 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1052 exit(1);
1054 mountpoint = argv[2];
1055 } else {
1056 mount_cifs_usage();
1057 exit(1);
1060 /* add sharename in opts string as unc= parm */
1062 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1063 longopts, NULL)) != -1) {
1064 switch (c) {
1065 /* No code to do the following options yet */
1066 /* case 'l':
1067 list_with_volumelabel = 1;
1068 break;
1069 case 'L':
1070 volumelabel = optarg;
1071 break; */
1072 /* case 'a':
1073 ++mount_all;
1074 break; */
1076 case '?':
1077 case 'h': /* help */
1078 mount_cifs_usage ();
1079 exit(1);
1080 case 'n':
1081 ++nomtab;
1082 break;
1083 case 'b':
1084 #ifdef MS_BIND
1085 flags |= MS_BIND;
1086 #else
1087 fprintf(stderr,
1088 "option 'b' (MS_BIND) not supported\n");
1089 #endif
1090 break;
1091 case 'm':
1092 #ifdef MS_MOVE
1093 flags |= MS_MOVE;
1094 #else
1095 fprintf(stderr,
1096 "option 'm' (MS_MOVE) not supported\n");
1097 #endif
1098 break;
1099 case 'o':
1100 orgoptions = strdup(optarg);
1101 break;
1102 case 'r': /* mount readonly */
1103 flags |= MS_RDONLY;
1104 break;
1105 case 'U':
1106 uuid = optarg;
1107 break;
1108 case 'v':
1109 ++verboseflag;
1110 break;
1111 case 'V':
1112 printf ("mount.cifs version: %s.%s%s\n",
1113 MOUNT_CIFS_VERSION_MAJOR,
1114 MOUNT_CIFS_VERSION_MINOR,
1115 MOUNT_CIFS_VENDOR_SUFFIX);
1116 exit (0);
1117 case 'w':
1118 flags &= ~MS_RDONLY;
1119 break;
1120 case 'R':
1121 rsize = atoi(optarg) ;
1122 break;
1123 case 'W':
1124 wsize = atoi(optarg);
1125 break;
1126 case '1':
1127 if (isdigit(*optarg)) {
1128 char *ep;
1130 uid = strtoul(optarg, &ep, 10);
1131 if (*ep) {
1132 printf("bad uid value \"%s\"\n", optarg);
1133 exit(1);
1135 } else {
1136 struct passwd *pw;
1138 if (!(pw = getpwnam(optarg))) {
1139 printf("bad user name \"%s\"\n", optarg);
1140 exit(1);
1142 uid = pw->pw_uid;
1143 endpwent();
1145 break;
1146 case '2':
1147 if (isdigit(*optarg)) {
1148 char *ep;
1150 gid = strtoul(optarg, &ep, 10);
1151 if (*ep) {
1152 printf("bad gid value \"%s\"\n", optarg);
1153 exit(1);
1155 } else {
1156 struct group *gr;
1158 if (!(gr = getgrnam(optarg))) {
1159 printf("bad user name \"%s\"\n", optarg);
1160 exit(1);
1162 gid = gr->gr_gid;
1163 endpwent();
1165 break;
1166 case 'u':
1167 got_user = 1;
1168 user_name = optarg;
1169 break;
1170 case 'd':
1171 domain_name = optarg; /* BB fix this - currently ignored */
1172 got_domain = 1;
1173 break;
1174 case 'p':
1175 if(mountpassword == NULL)
1176 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1177 if(mountpassword) {
1178 got_password = 1;
1179 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1181 break;
1182 case 'S':
1183 get_password_from_file(0 /* stdin */,NULL);
1184 break;
1185 case 't':
1186 break;
1187 default:
1188 printf("unknown mount option %c\n",c);
1189 mount_cifs_usage();
1190 exit(1);
1194 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1195 mount_cifs_usage();
1196 exit(1);
1199 if (getenv("PASSWD")) {
1200 if(mountpassword == NULL)
1201 mountpassword = (char *)calloc(65,1);
1202 if(mountpassword) {
1203 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE);
1204 got_password = 1;
1206 } else if (getenv("PASSWD_FD")) {
1207 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1208 } else if (getenv("PASSWD_FILE")) {
1209 get_password_from_file(0, getenv("PASSWD_FILE"));
1212 if (orgoptions && parse_options(&orgoptions, &flags)) {
1213 rc = -1;
1214 goto mount_exit;
1216 ipaddr = parse_server(&share_name);
1217 if((ipaddr == NULL) && (got_ip == 0)) {
1218 printf("No ip address specified and hostname not found\n");
1219 rc = -1;
1220 goto mount_exit;
1223 /* BB save off path and pop after mount returns? */
1224 resolved_path = (char *)malloc(PATH_MAX+1);
1225 if(resolved_path) {
1226 /* Note that if we can not canonicalize the name, we get
1227 another chance to see if it is valid when we chdir to it */
1228 if (realpath(mountpoint, resolved_path)) {
1229 mountpoint = resolved_path;
1232 if(chdir(mountpoint)) {
1233 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1234 rc = -1;
1235 goto mount_exit;
1238 if(stat (".", &statbuf)) {
1239 printf("mount error: mount point %s does not exist\n",mountpoint);
1240 rc = -1;
1241 goto mount_exit;
1244 if (S_ISDIR(statbuf.st_mode) == 0) {
1245 printf("mount error: mount point %s is not a directory\n",mountpoint);
1246 rc = -1;
1247 goto mount_exit;
1250 if((getuid() != 0) && (geteuid() == 0)) {
1251 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1252 #ifndef CIFS_ALLOW_USR_SUID
1253 /* Do not allow user mounts to control suid flag
1254 for mount unless explicitly built that way */
1255 flags |= MS_NOSUID | MS_NODEV;
1256 #endif
1257 } else {
1258 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1259 return -1;
1263 if(got_user == 0) {
1264 user_name = getusername();
1265 got_user = 1;
1268 if(got_password == 0) {
1269 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1270 no good replacement yet. */
1271 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1272 if (!tmp_pass || !mountpassword) {
1273 printf("Password not entered, exiting\n");
1274 return -1;
1276 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1277 got_password = 1;
1279 /* FIXME launch daemon (handles dfs name resolution and credential change)
1280 remember to clear parms and overwrite password field before launching */
1281 mount_retry:
1282 if(orgoptions) {
1283 optlen = strlen(orgoptions);
1284 orgoptlen = optlen;
1285 } else
1286 optlen = 0;
1287 if(share_name)
1288 optlen += strlen(share_name) + 4;
1289 else {
1290 printf("No server share name specified\n");
1291 printf("\nMounting the DFS root for server not implemented yet\n");
1292 exit(1);
1294 if(user_name)
1295 optlen += strlen(user_name) + 6;
1296 if(ipaddr)
1297 optlen += strlen(ipaddr) + 4;
1298 if(mountpassword)
1299 optlen += strlen(mountpassword) + 6;
1300 SAFE_FREE(options);
1301 options_size = optlen + 10 + DOMAIN_SIZE;
1302 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 */);
1304 if(options == NULL) {
1305 printf("Could not allocate memory for mount options\n");
1306 return -1;
1309 options[0] = 0;
1310 strlcpy(options,"unc=",options_size);
1311 strlcat(options,share_name,options_size);
1312 /* scan backwards and reverse direction of slash */
1313 temp = strrchr(options, '/');
1314 if(temp > options + 6)
1315 *temp = '\\';
1316 if(ipaddr) {
1317 strlcat(options,",ip=",options_size);
1318 strlcat(options,ipaddr,options_size);
1321 if(user_name) {
1322 /* check for syntax like user=domain\user */
1323 if(got_domain == 0)
1324 domain_name = check_for_domain(&user_name);
1325 strlcat(options,",user=",options_size);
1326 strlcat(options,user_name,options_size);
1328 if(retry == 0) {
1329 if(domain_name) {
1330 /* extra length accounted for in option string above */
1331 strlcat(options,",domain=",options_size);
1332 strlcat(options,domain_name,options_size);
1335 if(mountpassword) {
1336 /* Commas have to be doubled, or else they will
1337 look like the parameter separator */
1338 /* if(sep is not set)*/
1339 if(retry == 0)
1340 check_for_comma(&mountpassword);
1341 strlcat(options,",pass=",options_size);
1342 strlcat(options,mountpassword,options_size);
1345 strlcat(options,",ver=",options_size);
1346 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1348 if(orgoptions) {
1349 strlcat(options,",",options_size);
1350 strlcat(options,orgoptions,options_size);
1352 if(prefixpath) {
1353 strlcat(options,",prefixpath=",options_size);
1354 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1356 if(verboseflag)
1357 printf("\nmount.cifs kernel mount options %s \n",options);
1359 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1360 replace_char(dev_name, '\\', '/', strlen(share_name));
1362 if(mount(dev_name, mountpoint, "cifs", flags, options)) {
1363 /* remember to kill daemon on error */
1364 switch (errno) {
1365 case 0:
1366 printf("mount failed but no error number set\n");
1367 break;
1368 case ENODEV:
1369 printf("mount error: cifs filesystem not supported by the system\n");
1370 break;
1371 case ENXIO:
1372 if(retry == 0) {
1373 retry = 1;
1374 if (uppercase_string(dev_name) &&
1375 uppercase_string(share_name) &&
1376 uppercase_string(prefixpath)) {
1377 printf("retrying with upper case share name\n");
1378 goto mount_retry;
1381 default:
1382 printf("mount error %d = %s\n",errno,strerror(errno));
1384 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1385 rc = -1;
1386 goto mount_exit;
1387 } else {
1388 pmntfile = setmntent(MOUNTED, "a+");
1389 if(pmntfile) {
1390 mountent.mnt_fsname = dev_name;
1391 mountent.mnt_dir = mountpoint;
1392 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1393 mountent.mnt_opts = (char *)malloc(220);
1394 if(mountent.mnt_opts) {
1395 char * mount_user = getusername();
1396 memset(mountent.mnt_opts,0,200);
1397 if(flags & MS_RDONLY)
1398 strlcat(mountent.mnt_opts,"ro",220);
1399 else
1400 strlcat(mountent.mnt_opts,"rw",220);
1401 if(flags & MS_MANDLOCK)
1402 strlcat(mountent.mnt_opts,",mand",220);
1403 if(flags & MS_NOEXEC)
1404 strlcat(mountent.mnt_opts,",noexec",220);
1405 if(flags & MS_NOSUID)
1406 strlcat(mountent.mnt_opts,",nosuid",220);
1407 if(flags & MS_NODEV)
1408 strlcat(mountent.mnt_opts,",nodev",220);
1409 if(flags & MS_SYNCHRONOUS)
1410 strlcat(mountent.mnt_opts,",synch",220);
1411 if(mount_user) {
1412 if(getuid() != 0) {
1413 strlcat(mountent.mnt_opts,",user=",220);
1414 strlcat(mountent.mnt_opts,mount_user,220);
1416 /* free(mount_user); do not free static mem */
1419 mountent.mnt_freq = 0;
1420 mountent.mnt_passno = 0;
1421 rc = addmntent(pmntfile,&mountent);
1422 endmntent(pmntfile);
1423 SAFE_FREE(mountent.mnt_opts);
1424 } else {
1425 printf("could not update mount table\n");
1428 rc = 0;
1429 mount_exit:
1430 if(mountpassword) {
1431 int len = strlen(mountpassword);
1432 memset(mountpassword,0,len);
1433 SAFE_FREE(mountpassword);
1436 SAFE_FREE(options);
1437 SAFE_FREE(orgoptions);
1438 SAFE_FREE(resolved_path);
1439 SAFE_FREE(share_name);
1440 return rc;