packaging(RHEL): remove duplicate installation of pam_smbpass.so
[Samba.git] / source / client / mount.cifs.c
blobaf50cd9d474bb630f966cbc386353802720a8583
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 if (bufsize < (len1+1)) {
119 return ret;
121 len2 = bufsize - (len1+1);
123 if (len2 > 0) {
124 memcpy(d+len1, s, len2);
125 d[len1+len2] = 0;
127 return ret;
130 /* BB finish BB
132 cifs_umount
133 open nofollow - avoid symlink exposure?
134 get owner of dir see if matches self or if root
135 call system(umount argv) etc.
137 BB end finish BB */
139 static char * check_for_domain(char **);
142 static void mount_cifs_usage(void)
144 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
145 printf("\nMount the remote target, specified as a UNC name,");
146 printf(" to a local directory.\n\nOptions:\n");
147 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
148 printf("\nLess commonly used options:");
149 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
150 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
151 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
152 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
153 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
154 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
155 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
156 printf("\n\nRarely used options:");
157 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
158 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
159 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
160 printf("\n\tin6_addr");
161 printf("\n\nOptions are described in more detail in the manual page");
162 printf("\n\tman 8 mount.cifs\n");
163 printf("\nTo display the version number of the mount helper:");
164 printf("\n\t%s -V\n",thisprogram);
166 SAFE_FREE(mountpassword);
167 exit(1);
170 /* caller frees username if necessary */
171 static char * getusername(void) {
172 char *username = NULL;
173 struct passwd *password = getpwuid(getuid());
175 if (password) {
176 username = password->pw_name;
178 return username;
181 static char * parse_cifs_url(char * unc_name)
183 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
184 return NULL;
187 static int open_cred_file(char * file_name)
189 char * line_buf;
190 char * temp_val;
191 FILE * fs;
192 int i, length;
193 fs = fopen(file_name,"r");
194 if(fs == NULL)
195 return errno;
196 line_buf = (char *)malloc(4096);
197 if(line_buf == NULL) {
198 fclose(fs);
199 return ENOMEM;
202 while(fgets(line_buf,4096,fs)) {
203 /* parse line from credential file */
205 /* eat leading white space */
206 for(i=0;i<4086;i++) {
207 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
208 break;
209 /* if whitespace - skip past it */
211 if (strncasecmp("username",line_buf+i,8) == 0) {
212 temp_val = strchr(line_buf + i,'=');
213 if(temp_val) {
214 /* go past equals sign */
215 temp_val++;
216 for(length = 0;length<4087;length++) {
217 if ((temp_val[length] == '\n')
218 || (temp_val[length] == '\0')) {
219 temp_val[length] = '\0';
220 break;
223 if(length > 4086) {
224 printf("mount.cifs failed due to malformed username in credentials file");
225 memset(line_buf,0,4096);
226 exit(1);
227 } else {
228 got_user = 1;
229 user_name = (char *)calloc(1 + length,1);
230 /* BB adding free of user_name string before exit,
231 not really necessary but would be cleaner */
232 strlcpy(user_name,temp_val, length+1);
235 } else if (strncasecmp("password",line_buf+i,8) == 0) {
236 temp_val = strchr(line_buf+i,'=');
237 if(temp_val) {
238 /* go past equals sign */
239 temp_val++;
240 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
241 if ((temp_val[length] == '\n')
242 || (temp_val[length] == '\0')) {
243 temp_val[length] = '\0';
244 break;
247 if(length > MOUNT_PASSWD_SIZE) {
248 printf("mount.cifs failed: password in credentials file too long\n");
249 memset(line_buf,0, 4096);
250 exit(1);
251 } else {
252 if(mountpassword == NULL) {
253 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
254 } else
255 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
256 if(mountpassword) {
257 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
258 got_password = 1;
262 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
263 temp_val = strchr(line_buf+i,'=');
264 if(temp_val) {
265 /* go past equals sign */
266 temp_val++;
267 if(verboseflag)
268 printf("\nDomain %s\n",temp_val);
269 for(length = 0;length<DOMAIN_SIZE+1;length++) {
270 if ((temp_val[length] == '\n')
271 || (temp_val[length] == '\0')) {
272 temp_val[length] = '\0';
273 break;
276 if(length > DOMAIN_SIZE) {
277 printf("mount.cifs failed: domain in credentials file too long\n");
278 exit(1);
279 } else {
280 if(domain_name == NULL) {
281 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
282 } else
283 memset(domain_name,0,DOMAIN_SIZE);
284 if(domain_name) {
285 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
286 got_domain = 1;
293 fclose(fs);
294 SAFE_FREE(line_buf);
295 return 0;
298 static int get_password_from_file(int file_descript, char * filename)
300 int rc = 0;
301 int i;
302 char c;
304 if(mountpassword == NULL)
305 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
306 else
307 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
309 if (mountpassword == NULL) {
310 printf("malloc failed\n");
311 exit(1);
314 if(filename != NULL) {
315 file_descript = open(filename, O_RDONLY);
316 if(file_descript < 0) {
317 printf("mount.cifs failed. %s attempting to open password file %s\n",
318 strerror(errno),filename);
319 exit(1);
322 /* else file already open and fd provided */
324 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
325 rc = read(file_descript,&c,1);
326 if(rc < 0) {
327 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
328 if(filename != NULL)
329 close(file_descript);
330 exit(1);
331 } else if(rc == 0) {
332 if(mountpassword[0] == 0) {
333 if(verboseflag)
334 printf("\nWarning: null password used since cifs password file empty");
336 break;
337 } else /* read valid character */ {
338 if((c == 0) || (c == '\n')) {
339 mountpassword[i] = '\0';
340 break;
341 } else
342 mountpassword[i] = c;
345 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
346 printf("\nWarning: password longer than %d characters specified in cifs password file",
347 MOUNT_PASSWD_SIZE);
349 got_password = 1;
350 if(filename != NULL) {
351 close(file_descript);
354 return rc;
357 static int parse_options(char ** optionsp, int * filesys_flags)
359 const char * data;
360 char * percent_char = NULL;
361 char * value = NULL;
362 char * next_keyword = NULL;
363 char * out = NULL;
364 int out_len = 0;
365 int word_len;
366 int rc = 0;
367 char user[32];
368 char group[32];
370 if (!optionsp || !*optionsp)
371 return 1;
372 data = *optionsp;
374 if(verboseflag)
375 printf("parsing options: %s\n", data);
377 /* BB fixme check for separator override BB */
379 if (getuid()) {
380 got_uid = 1;
381 snprintf(user,sizeof(user),"%u",getuid());
382 got_gid = 1;
383 snprintf(group,sizeof(group),"%u",getgid());
386 /* while ((data = strsep(&options, ",")) != NULL) { */
387 while(data != NULL) {
388 /* check if ends with trailing comma */
389 if(*data == 0)
390 break;
392 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
393 /* data = next keyword */
394 /* value = next value ie stuff after equal sign */
396 next_keyword = strchr(data,','); /* BB handle sep= */
398 /* temporarily null terminate end of keyword=value pair */
399 if(next_keyword)
400 *next_keyword++ = 0;
402 /* temporarily null terminate keyword to make keyword and value distinct */
403 if ((value = strchr(data, '=')) != NULL) {
404 *value = '\0';
405 value++;
408 if (strncmp(data, "users",5) == 0) {
409 if(!value || !*value) {
410 goto nocopy;
412 } else if (strncmp(data, "user_xattr",10) == 0) {
413 /* do nothing - need to skip so not parsed as user name */
414 } else if (strncmp(data, "user", 4) == 0) {
416 if (!value || !*value) {
417 if(data[4] == '\0') {
418 if(verboseflag)
419 printf("\nskipping empty user mount parameter\n");
420 /* remove the parm since it would otherwise be confusing
421 to the kernel code which would think it was a real username */
422 goto nocopy;
423 } else {
424 printf("username specified with no parameter\n");
425 return 1; /* needs_arg; */
427 } else {
428 if (strnlen(value, 260) < 260) {
429 got_user=1;
430 percent_char = strchr(value,'%');
431 if(percent_char) {
432 *percent_char = ',';
433 if(mountpassword == NULL)
434 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
435 if(mountpassword) {
436 if(got_password)
437 printf("\nmount.cifs warning - password specified twice\n");
438 got_password = 1;
439 percent_char++;
440 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
441 /* remove password from username */
442 while(*percent_char != 0) {
443 *percent_char = ',';
444 percent_char++;
448 /* this is only case in which the user
449 name buf is not malloc - so we have to
450 check for domain name embedded within
451 the user name here since the later
452 call to check_for_domain will not be
453 invoked */
454 domain_name = check_for_domain(&value);
455 } else {
456 printf("username too long\n");
457 return 1;
460 } else if (strncmp(data, "pass", 4) == 0) {
461 if (!value || !*value) {
462 if(got_password) {
463 printf("\npassword specified twice, ignoring second\n");
464 } else
465 got_password = 1;
466 } else if (strnlen(value, 17) < 17) {
467 if(got_password)
468 printf("\nmount.cifs warning - password specified twice\n");
469 got_password = 1;
470 } else {
471 printf("password too long\n");
472 return 1;
474 } else if (strncmp(data, "sec", 3) == 0) {
475 if (value) {
476 if (!strcmp(value, "none"))
477 got_password = 1;
479 } else if (strncmp(data, "ip", 2) == 0) {
480 if (!value || !*value) {
481 printf("target ip address argument missing");
482 } else if (strnlen(value, 35) < 35) {
483 if(verboseflag)
484 printf("ip address %s override specified\n",value);
485 got_ip = 1;
486 } else {
487 printf("ip address too long\n");
488 return 1;
490 } else if ((strncmp(data, "unc", 3) == 0)
491 || (strncmp(data, "target", 6) == 0)
492 || (strncmp(data, "path", 4) == 0)) {
493 if (!value || !*value) {
494 printf("invalid path to network resource\n");
495 return 1; /* needs_arg; */
496 } else if(strnlen(value,5) < 5) {
497 printf("UNC name too short");
500 if (strnlen(value, 300) < 300) {
501 got_unc = 1;
502 if (strncmp(value, "//", 2) == 0) {
503 if(got_unc)
504 printf("unc name specified twice, ignoring second\n");
505 else
506 got_unc = 1;
507 } else if (strncmp(value, "\\\\", 2) != 0) {
508 printf("UNC Path does not begin with // or \\\\ \n");
509 return 1;
510 } else {
511 if(got_unc)
512 printf("unc name specified twice, ignoring second\n");
513 else
514 got_unc = 1;
516 } else {
517 printf("CIFS: UNC name too long\n");
518 return 1;
520 } else if ((strncmp(data, "domain", 3) == 0)
521 || (strncmp(data, "workgroup", 5) == 0)) {
522 if (!value || !*value) {
523 printf("CIFS: invalid domain name\n");
524 return 1; /* needs_arg; */
526 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
527 got_domain = 1;
528 } else {
529 printf("domain name too long\n");
530 return 1;
532 } else if (strncmp(data, "cred", 4) == 0) {
533 if (value && *value) {
534 rc = open_cred_file(value);
535 if(rc) {
536 printf("error %d (%s) opening credential file %s\n",
537 rc, strerror(rc), value);
538 return 1;
540 } else {
541 printf("invalid credential file name specified\n");
542 return 1;
544 } else if (strncmp(data, "uid", 3) == 0) {
545 if (value && *value) {
546 got_uid = 1;
547 if (!isdigit(*value)) {
548 struct passwd *pw;
550 if (!(pw = getpwnam(value))) {
551 printf("bad user name \"%s\"\n", value);
552 exit(1);
554 snprintf(user, sizeof(user), "%u", pw->pw_uid);
555 } else {
556 strlcpy(user,value,sizeof(user));
559 goto nocopy;
560 } else if (strncmp(data, "gid", 3) == 0) {
561 if (value && *value) {
562 got_gid = 1;
563 if (!isdigit(*value)) {
564 struct group *gr;
566 if (!(gr = getgrnam(value))) {
567 printf("bad group name \"%s\"\n", value);
568 exit(1);
570 snprintf(group, sizeof(group), "%u", gr->gr_gid);
571 } else {
572 strlcpy(group,value,sizeof(group));
575 goto nocopy;
576 /* fmask and dmask synonyms for people used to smbfs syntax */
577 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
578 if (!value || !*value) {
579 printf ("Option '%s' requires a numerical argument\n", data);
580 return 1;
583 if (value[0] != '0') {
584 printf ("WARNING: '%s' not expressed in octal.\n", data);
587 if (strcmp (data, "fmask") == 0) {
588 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
589 data = "file_mode"; /* BB fix this */
591 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
592 if (!value || !*value) {
593 printf ("Option '%s' requires a numerical argument\n", data);
594 return 1;
597 if (value[0] != '0') {
598 printf ("WARNING: '%s' not expressed in octal.\n", data);
601 if (strcmp (data, "dmask") == 0) {
602 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
603 data = "dir_mode";
605 /* the following eight mount options should be
606 stripped out from what is passed into the kernel
607 since these eight options are best passed as the
608 mount flags rather than redundantly to the kernel
609 and could generate spurious warnings depending on the
610 level of the corresponding cifs vfs kernel code */
611 } else if (strncmp(data, "nosuid", 6) == 0) {
612 *filesys_flags |= MS_NOSUID;
613 } else if (strncmp(data, "suid", 4) == 0) {
614 *filesys_flags &= ~MS_NOSUID;
615 } else if (strncmp(data, "nodev", 5) == 0) {
616 *filesys_flags |= MS_NODEV;
617 } else if ((strncmp(data, "nobrl", 5) == 0) ||
618 (strncmp(data, "nolock", 6) == 0)) {
619 *filesys_flags &= ~MS_MANDLOCK;
620 } else if (strncmp(data, "dev", 3) == 0) {
621 *filesys_flags &= ~MS_NODEV;
622 } else if (strncmp(data, "noexec", 6) == 0) {
623 *filesys_flags |= MS_NOEXEC;
624 } else if (strncmp(data, "exec", 4) == 0) {
625 *filesys_flags &= ~MS_NOEXEC;
626 } else if (strncmp(data, "guest", 5) == 0) {
627 got_password=1;
628 } else if (strncmp(data, "ro", 2) == 0) {
629 *filesys_flags |= MS_RDONLY;
630 } else if (strncmp(data, "rw", 2) == 0) {
631 *filesys_flags &= ~MS_RDONLY;
632 } else if (strncmp(data, "remount", 7) == 0) {
633 *filesys_flags |= MS_REMOUNT;
634 } /* else if (strnicmp(data, "port", 4) == 0) {
635 if (value && *value) {
636 vol->port =
637 simple_strtoul(value, &value, 0);
639 } else if (strnicmp(data, "rsize", 5) == 0) {
640 if (value && *value) {
641 vol->rsize =
642 simple_strtoul(value, &value, 0);
644 } else if (strnicmp(data, "wsize", 5) == 0) {
645 if (value && *value) {
646 vol->wsize =
647 simple_strtoul(value, &value, 0);
649 } else if (strnicmp(data, "version", 3) == 0) {
650 } else {
651 printf("CIFS: Unknown mount option %s\n",data);
652 } */ /* nothing to do on those four mount options above.
653 Just pass to kernel and ignore them here */
655 /* Copy (possibly modified) option to out */
656 word_len = strlen(data);
657 if (value)
658 word_len += 1 + strlen(value);
660 out = (char *)realloc(out, out_len + word_len + 2);
661 if (out == NULL) {
662 perror("malloc");
663 exit(1);
666 if (out_len) {
667 strlcat(out, ",", out_len + word_len + 2);
668 out_len++;
671 if (value)
672 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
673 else
674 snprintf(out + out_len, word_len + 1, "%s", data);
675 out_len = strlen(out);
677 nocopy:
678 data = next_keyword;
681 /* special-case the uid and gid */
682 if (got_uid) {
683 word_len = strlen(user);
685 out = (char *)realloc(out, out_len + word_len + 6);
686 if (out == NULL) {
687 perror("malloc");
688 exit(1);
691 if (out_len) {
692 strlcat(out, ",", out_len + word_len + 6);
693 out_len++;
695 snprintf(out + out_len, word_len + 5, "uid=%s", user);
696 out_len = strlen(out);
698 if (got_gid) {
699 word_len = strlen(group);
701 out = (char *)realloc(out, out_len + 1 + word_len + 6);
702 if (out == NULL) {
703 perror("malloc");
704 exit(1);
707 if (out_len) {
708 strlcat(out, ",", out_len + word_len + 6);
709 out_len++;
711 snprintf(out + out_len, word_len + 5, "gid=%s", group);
712 out_len = strlen(out);
715 SAFE_FREE(*optionsp);
716 *optionsp = out;
717 return 0;
720 /* replace all (one or more) commas with double commas */
721 static void check_for_comma(char ** ppasswrd)
723 char *new_pass_buf;
724 char *pass;
725 int i,j;
726 int number_of_commas = 0;
727 int len;
729 if(ppasswrd == NULL)
730 return;
731 else
732 (pass = *ppasswrd);
734 len = strlen(pass);
736 for(i=0;i<len;i++) {
737 if(pass[i] == ',')
738 number_of_commas++;
741 if(number_of_commas == 0)
742 return;
743 if(number_of_commas > MOUNT_PASSWD_SIZE) {
744 /* would otherwise overflow the mount options buffer */
745 printf("\nInvalid password. Password contains too many commas.\n");
746 return;
749 new_pass_buf = (char *)malloc(len+number_of_commas+1);
750 if(new_pass_buf == NULL)
751 return;
753 for(i=0,j=0;i<len;i++,j++) {
754 new_pass_buf[j] = pass[i];
755 if(pass[i] == ',') {
756 j++;
757 new_pass_buf[j] = pass[i];
760 new_pass_buf[len+number_of_commas] = 0;
762 SAFE_FREE(*ppasswrd);
763 *ppasswrd = new_pass_buf;
765 return;
768 /* Usernames can not have backslash in them and we use
769 [BB check if usernames can have forward slash in them BB]
770 backslash as domain\user separator character
772 static char * check_for_domain(char **ppuser)
774 char * original_string;
775 char * usernm;
776 char * domainnm;
777 int original_len;
778 int len;
779 int i;
781 if(ppuser == NULL)
782 return NULL;
784 original_string = *ppuser;
786 if (original_string == NULL)
787 return NULL;
789 original_len = strlen(original_string);
791 usernm = strchr(*ppuser,'/');
792 if (usernm == NULL) {
793 usernm = strchr(*ppuser,'\\');
794 if (usernm == NULL)
795 return NULL;
798 if(got_domain) {
799 printf("Domain name specified twice. Username probably malformed\n");
800 return NULL;
803 usernm[0] = 0;
804 domainnm = *ppuser;
805 if (domainnm[0] != 0) {
806 got_domain = 1;
807 } else {
808 printf("null domain\n");
810 len = strlen(domainnm);
811 /* reset domainm to new buffer, and copy
812 domain name into it */
813 domainnm = (char *)malloc(len+1);
814 if(domainnm == NULL)
815 return NULL;
817 strlcpy(domainnm,*ppuser,len+1);
819 /* move_string(*ppuser, usernm+1) */
820 len = strlen(usernm+1);
822 if(len >= original_len) {
823 /* should not happen */
824 return domainnm;
827 for(i=0;i<original_len;i++) {
828 if(i<len)
829 original_string[i] = usernm[i+1];
830 else /* stuff with commas to remove last parm */
831 original_string[i] = ',';
834 /* BB add check for more than one slash?
835 strchr(*ppuser,'/');
836 strchr(*ppuser,'\\')
839 return domainnm;
842 /* replace all occurances of "from" in a string with "to" */
843 static void replace_char(char *string, char from, char to, int maxlen)
845 char *lastchar = string + maxlen;
846 while (string) {
847 string = strchr(string, from);
848 if (string) {
849 *string = to;
850 if (string >= lastchar)
851 return;
856 /* Note that caller frees the returned buffer if necessary */
857 static char * parse_server(char ** punc_name)
859 char * unc_name = *punc_name;
860 int length = strnlen(unc_name, MAX_UNC_LEN);
861 char * share;
862 char * ipaddress_string = NULL;
863 struct hostent * host_entry = NULL;
864 struct in_addr server_ipaddr;
866 if(length > (MAX_UNC_LEN - 1)) {
867 printf("mount error: UNC name too long");
868 return NULL;
870 if (strncasecmp("cifs://",unc_name,7) == 0)
871 return parse_cifs_url(unc_name+7);
872 if (strncasecmp("smb://",unc_name,6) == 0) {
873 return parse_cifs_url(unc_name+6);
876 if(length < 3) {
877 /* BB add code to find DFS root here */
878 printf("\nMounting the DFS root for domain not implemented yet\n");
879 return NULL;
880 } else {
881 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
882 /* check for nfs syntax ie server:share */
883 share = strchr(unc_name,':');
884 if(share) {
885 *punc_name = (char *)malloc(length+3);
886 if(*punc_name == NULL) {
887 /* put the original string back if
888 no memory left */
889 *punc_name = unc_name;
890 return NULL;
892 *share = '/';
893 strlcpy((*punc_name)+2,unc_name,length+1);
894 SAFE_FREE(unc_name);
895 unc_name = *punc_name;
896 unc_name[length+2] = 0;
897 goto continue_unc_parsing;
898 } else {
899 printf("mount error: improperly formatted UNC name.");
900 printf(" %s does not begin with \\\\ or //\n",unc_name);
901 return NULL;
903 } else {
904 continue_unc_parsing:
905 unc_name[0] = '/';
906 unc_name[1] = '/';
907 unc_name += 2;
909 /* allow for either delimiter between host and sharename */
910 if ((share = strpbrk(unc_name, "/\\"))) {
911 *share = 0; /* temporarily terminate the string */
912 share += 1;
913 if(got_ip == 0) {
914 host_entry = gethostbyname(unc_name);
916 *(share - 1) = '/'; /* put delimiter back */
918 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
919 if ((prefixpath = strpbrk(share, "/\\"))) {
920 *prefixpath = 0; /* permanently terminate the string */
921 if (!strlen(++prefixpath))
922 prefixpath = NULL; /* this needs to be done explicitly */
924 if(got_ip) {
925 if(verboseflag)
926 printf("ip address specified explicitly\n");
927 return NULL;
929 if(host_entry == NULL) {
930 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
931 return NULL;
932 } else {
933 /* BB should we pass an alternate version of the share name as Unicode */
934 /* BB what about ipv6? BB */
935 /* BB add retries with alternate servers in list */
937 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
939 ipaddress_string = inet_ntoa(server_ipaddr);
940 if(ipaddress_string == NULL) {
941 printf("mount error: could not get valid ip address for target server\n");
942 return NULL;
944 return ipaddress_string;
946 } else {
947 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
948 printf("Mounting the DFS root for a particular server not implemented yet\n");
949 return NULL;
955 static struct option longopts[] = {
956 { "all", 0, NULL, 'a' },
957 { "help",0, NULL, 'h' },
958 { "move",0, NULL, 'm' },
959 { "bind",0, NULL, 'b' },
960 { "read-only", 0, NULL, 'r' },
961 { "ro", 0, NULL, 'r' },
962 { "verbose", 0, NULL, 'v' },
963 { "version", 0, NULL, 'V' },
964 { "read-write", 0, NULL, 'w' },
965 { "rw", 0, NULL, 'w' },
966 { "options", 1, NULL, 'o' },
967 { "type", 1, NULL, 't' },
968 { "rsize",1, NULL, 'R' },
969 { "wsize",1, NULL, 'W' },
970 { "uid", 1, NULL, '1'},
971 { "gid", 1, NULL, '2'},
972 { "user",1,NULL,'u'},
973 { "username",1,NULL,'u'},
974 { "dom",1,NULL,'d'},
975 { "domain",1,NULL,'d'},
976 { "password",1,NULL,'p'},
977 { "pass",1,NULL,'p'},
978 { "credentials",1,NULL,'c'},
979 { "port",1,NULL,'P'},
980 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
981 { NULL, 0, NULL, 0 }
984 /* convert a string to uppercase. return false if the string
985 * wasn't ASCII or was a NULL ptr */
986 static int
987 uppercase_string(char *string)
989 if (!string)
990 return 0;
992 while (*string) {
993 /* check for unicode */
994 if ((unsigned char) string[0] & 0x80)
995 return 0;
996 *string = toupper((unsigned char) *string);
997 string++;
1000 return 1;
1003 int main(int argc, char ** argv)
1005 int c;
1006 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1007 char * orgoptions = NULL;
1008 char * share_name = NULL;
1009 char * ipaddr = NULL;
1010 char * uuid = NULL;
1011 char * mountpoint = NULL;
1012 char * options = NULL;
1013 char * resolved_path = NULL;
1014 char * temp;
1015 char * dev_name;
1016 int rc;
1017 int rsize = 0;
1018 int wsize = 0;
1019 int nomtab = 0;
1020 int uid = 0;
1021 int gid = 0;
1022 int optlen = 0;
1023 int orgoptlen = 0;
1024 size_t options_size = 0;
1025 int retry = 0; /* set when we have to retry mount with uppercase */
1026 struct stat statbuf;
1027 struct utsname sysinfo;
1028 struct mntent mountent;
1029 FILE * pmntfile;
1031 /* setlocale(LC_ALL, "");
1032 bindtextdomain(PACKAGE, LOCALEDIR);
1033 textdomain(PACKAGE); */
1035 if(argc && argv) {
1036 thisprogram = argv[0];
1037 } else {
1038 mount_cifs_usage();
1039 exit(1);
1042 if(thisprogram == NULL)
1043 thisprogram = "mount.cifs";
1045 uname(&sysinfo);
1046 /* BB add workstation name and domain and pass down */
1048 /* #ifdef _GNU_SOURCE
1049 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1050 #endif */
1051 if(argc > 2) {
1052 dev_name = argv[1];
1053 share_name = strndup(argv[1], MAX_UNC_LEN);
1054 if (share_name == NULL) {
1055 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1056 exit(1);
1058 mountpoint = argv[2];
1059 } else {
1060 mount_cifs_usage();
1061 exit(1);
1064 /* add sharename in opts string as unc= parm */
1066 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1067 longopts, NULL)) != -1) {
1068 switch (c) {
1069 /* No code to do the following options yet */
1070 /* case 'l':
1071 list_with_volumelabel = 1;
1072 break;
1073 case 'L':
1074 volumelabel = optarg;
1075 break; */
1076 /* case 'a':
1077 ++mount_all;
1078 break; */
1080 case '?':
1081 case 'h': /* help */
1082 mount_cifs_usage ();
1083 exit(1);
1084 case 'n':
1085 ++nomtab;
1086 break;
1087 case 'b':
1088 #ifdef MS_BIND
1089 flags |= MS_BIND;
1090 #else
1091 fprintf(stderr,
1092 "option 'b' (MS_BIND) not supported\n");
1093 #endif
1094 break;
1095 case 'm':
1096 #ifdef MS_MOVE
1097 flags |= MS_MOVE;
1098 #else
1099 fprintf(stderr,
1100 "option 'm' (MS_MOVE) not supported\n");
1101 #endif
1102 break;
1103 case 'o':
1104 orgoptions = strdup(optarg);
1105 break;
1106 case 'r': /* mount readonly */
1107 flags |= MS_RDONLY;
1108 break;
1109 case 'U':
1110 uuid = optarg;
1111 break;
1112 case 'v':
1113 ++verboseflag;
1114 break;
1115 case 'V':
1116 printf ("mount.cifs version: %s.%s%s\n",
1117 MOUNT_CIFS_VERSION_MAJOR,
1118 MOUNT_CIFS_VERSION_MINOR,
1119 MOUNT_CIFS_VENDOR_SUFFIX);
1120 exit (0);
1121 case 'w':
1122 flags &= ~MS_RDONLY;
1123 break;
1124 case 'R':
1125 rsize = atoi(optarg) ;
1126 break;
1127 case 'W':
1128 wsize = atoi(optarg);
1129 break;
1130 case '1':
1131 if (isdigit(*optarg)) {
1132 char *ep;
1134 uid = strtoul(optarg, &ep, 10);
1135 if (*ep) {
1136 printf("bad uid value \"%s\"\n", optarg);
1137 exit(1);
1139 } else {
1140 struct passwd *pw;
1142 if (!(pw = getpwnam(optarg))) {
1143 printf("bad user name \"%s\"\n", optarg);
1144 exit(1);
1146 uid = pw->pw_uid;
1147 endpwent();
1149 break;
1150 case '2':
1151 if (isdigit(*optarg)) {
1152 char *ep;
1154 gid = strtoul(optarg, &ep, 10);
1155 if (*ep) {
1156 printf("bad gid value \"%s\"\n", optarg);
1157 exit(1);
1159 } else {
1160 struct group *gr;
1162 if (!(gr = getgrnam(optarg))) {
1163 printf("bad user name \"%s\"\n", optarg);
1164 exit(1);
1166 gid = gr->gr_gid;
1167 endpwent();
1169 break;
1170 case 'u':
1171 got_user = 1;
1172 user_name = optarg;
1173 break;
1174 case 'd':
1175 domain_name = optarg; /* BB fix this - currently ignored */
1176 got_domain = 1;
1177 break;
1178 case 'p':
1179 if(mountpassword == NULL)
1180 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1181 if(mountpassword) {
1182 got_password = 1;
1183 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1185 break;
1186 case 'S':
1187 get_password_from_file(0 /* stdin */,NULL);
1188 break;
1189 case 't':
1190 break;
1191 default:
1192 printf("unknown mount option %c\n",c);
1193 mount_cifs_usage();
1194 exit(1);
1198 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1199 mount_cifs_usage();
1200 exit(1);
1203 if (getenv("PASSWD")) {
1204 if(mountpassword == NULL)
1205 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1206 if(mountpassword) {
1207 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1208 got_password = 1;
1210 } else if (getenv("PASSWD_FD")) {
1211 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1212 } else if (getenv("PASSWD_FILE")) {
1213 get_password_from_file(0, getenv("PASSWD_FILE"));
1216 if (orgoptions && parse_options(&orgoptions, &flags)) {
1217 rc = -1;
1218 goto mount_exit;
1220 ipaddr = parse_server(&share_name);
1221 if((ipaddr == NULL) && (got_ip == 0)) {
1222 printf("No ip address specified and hostname not found\n");
1223 rc = -1;
1224 goto mount_exit;
1227 /* BB save off path and pop after mount returns? */
1228 resolved_path = (char *)malloc(PATH_MAX+1);
1229 if(resolved_path) {
1230 /* Note that if we can not canonicalize the name, we get
1231 another chance to see if it is valid when we chdir to it */
1232 if (realpath(mountpoint, resolved_path)) {
1233 mountpoint = resolved_path;
1236 if(chdir(mountpoint)) {
1237 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1238 rc = -1;
1239 goto mount_exit;
1242 if(stat (".", &statbuf)) {
1243 printf("mount error: mount point %s does not exist\n",mountpoint);
1244 rc = -1;
1245 goto mount_exit;
1248 if (S_ISDIR(statbuf.st_mode) == 0) {
1249 printf("mount error: mount point %s is not a directory\n",mountpoint);
1250 rc = -1;
1251 goto mount_exit;
1254 if((getuid() != 0) && (geteuid() == 0)) {
1255 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1256 #ifndef CIFS_ALLOW_USR_SUID
1257 /* Do not allow user mounts to control suid flag
1258 for mount unless explicitly built that way */
1259 flags |= MS_NOSUID | MS_NODEV;
1260 #endif
1261 } else {
1262 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1263 return -1;
1267 if(got_user == 0) {
1268 user_name = getusername();
1269 got_user = 1;
1272 if(got_password == 0) {
1273 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1274 no good replacement yet. */
1275 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1276 if (!tmp_pass || !mountpassword) {
1277 printf("Password not entered, exiting\n");
1278 return -1;
1280 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1281 got_password = 1;
1283 /* FIXME launch daemon (handles dfs name resolution and credential change)
1284 remember to clear parms and overwrite password field before launching */
1285 mount_retry:
1286 if(orgoptions) {
1287 optlen = strlen(orgoptions);
1288 orgoptlen = optlen;
1289 } else
1290 optlen = 0;
1291 if(share_name)
1292 optlen += strlen(share_name) + 4;
1293 else {
1294 printf("No server share name specified\n");
1295 printf("\nMounting the DFS root for server not implemented yet\n");
1296 exit(1);
1298 if(user_name)
1299 optlen += strlen(user_name) + 6;
1300 if(ipaddr)
1301 optlen += strlen(ipaddr) + 4;
1302 if(mountpassword)
1303 optlen += strlen(mountpassword) + 6;
1304 SAFE_FREE(options);
1305 options_size = optlen + 10 + DOMAIN_SIZE;
1306 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 */);
1308 if(options == NULL) {
1309 printf("Could not allocate memory for mount options\n");
1310 return -1;
1313 options[0] = 0;
1314 strlcpy(options,"unc=",options_size);
1315 strlcat(options,share_name,options_size);
1316 /* scan backwards and reverse direction of slash */
1317 temp = strrchr(options, '/');
1318 if(temp > options + 6)
1319 *temp = '\\';
1320 if(ipaddr) {
1321 strlcat(options,",ip=",options_size);
1322 strlcat(options,ipaddr,options_size);
1325 if(user_name) {
1326 /* check for syntax like user=domain\user */
1327 if(got_domain == 0)
1328 domain_name = check_for_domain(&user_name);
1329 strlcat(options,",user=",options_size);
1330 strlcat(options,user_name,options_size);
1332 if(retry == 0) {
1333 if(domain_name) {
1334 /* extra length accounted for in option string above */
1335 strlcat(options,",domain=",options_size);
1336 strlcat(options,domain_name,options_size);
1339 if(mountpassword) {
1340 /* Commas have to be doubled, or else they will
1341 look like the parameter separator */
1342 /* if(sep is not set)*/
1343 if(retry == 0)
1344 check_for_comma(&mountpassword);
1345 strlcat(options,",pass=",options_size);
1346 strlcat(options,mountpassword,options_size);
1349 strlcat(options,",ver=",options_size);
1350 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1352 if(orgoptions) {
1353 strlcat(options,",",options_size);
1354 strlcat(options,orgoptions,options_size);
1356 if(prefixpath) {
1357 strlcat(options,",prefixpath=",options_size);
1358 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1360 if(verboseflag)
1361 printf("\nmount.cifs kernel mount options %s \n",options);
1363 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1364 replace_char(dev_name, '\\', '/', strlen(share_name));
1366 if(mount(dev_name, mountpoint, "cifs", flags, options)) {
1367 /* remember to kill daemon on error */
1368 switch (errno) {
1369 case 0:
1370 printf("mount failed but no error number set\n");
1371 break;
1372 case ENODEV:
1373 printf("mount error: cifs filesystem not supported by the system\n");
1374 break;
1375 case ENXIO:
1376 if(retry == 0) {
1377 retry = 1;
1378 if (uppercase_string(dev_name) &&
1379 uppercase_string(share_name) &&
1380 uppercase_string(prefixpath)) {
1381 printf("retrying with upper case share name\n");
1382 goto mount_retry;
1385 default:
1386 printf("mount error %d = %s\n",errno,strerror(errno));
1388 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1389 rc = -1;
1390 goto mount_exit;
1391 } else {
1392 pmntfile = setmntent(MOUNTED, "a+");
1393 if(pmntfile) {
1394 mountent.mnt_fsname = dev_name;
1395 mountent.mnt_dir = mountpoint;
1396 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1397 mountent.mnt_opts = (char *)malloc(220);
1398 if(mountent.mnt_opts) {
1399 char * mount_user = getusername();
1400 memset(mountent.mnt_opts,0,200);
1401 if(flags & MS_RDONLY)
1402 strlcat(mountent.mnt_opts,"ro",220);
1403 else
1404 strlcat(mountent.mnt_opts,"rw",220);
1405 if(flags & MS_MANDLOCK)
1406 strlcat(mountent.mnt_opts,",mand",220);
1407 if(flags & MS_NOEXEC)
1408 strlcat(mountent.mnt_opts,",noexec",220);
1409 if(flags & MS_NOSUID)
1410 strlcat(mountent.mnt_opts,",nosuid",220);
1411 if(flags & MS_NODEV)
1412 strlcat(mountent.mnt_opts,",nodev",220);
1413 if(flags & MS_SYNCHRONOUS)
1414 strlcat(mountent.mnt_opts,",synch",220);
1415 if(mount_user) {
1416 if(getuid() != 0) {
1417 strlcat(mountent.mnt_opts,",user=",220);
1418 strlcat(mountent.mnt_opts,mount_user,220);
1420 /* free(mount_user); do not free static mem */
1423 mountent.mnt_freq = 0;
1424 mountent.mnt_passno = 0;
1425 rc = addmntent(pmntfile,&mountent);
1426 endmntent(pmntfile);
1427 SAFE_FREE(mountent.mnt_opts);
1428 } else {
1429 printf("could not update mount table\n");
1432 rc = 0;
1433 mount_exit:
1434 if(mountpassword) {
1435 int len = strlen(mountpassword);
1436 memset(mountpassword,0,len);
1437 SAFE_FREE(mountpassword);
1440 SAFE_FREE(options);
1441 SAFE_FREE(orgoptions);
1442 SAFE_FREE(resolved_path);
1443 SAFE_FREE(share_name);
1444 return rc;