mount.cifs: make local versions of strlcat and strlcpy static
[Samba/wip.git] / source3 / client / mount.cifs.c
blob3b56e5f861a6a2cbb05721ed17b982f0b9e80b75
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 static 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 static 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 (!strncmp(value, "none", 4) ||
477 !strncmp(value, "krb5", 4))
478 got_password = 1;
480 } else if (strncmp(data, "ip", 2) == 0) {
481 if (!value || !*value) {
482 printf("target ip address argument missing");
483 } else if (strnlen(value, 35) < 35) {
484 if(verboseflag)
485 printf("ip address %s override specified\n",value);
486 got_ip = 1;
487 } else {
488 printf("ip address too long\n");
489 return 1;
491 } else if ((strncmp(data, "unc", 3) == 0)
492 || (strncmp(data, "target", 6) == 0)
493 || (strncmp(data, "path", 4) == 0)) {
494 if (!value || !*value) {
495 printf("invalid path to network resource\n");
496 return 1; /* needs_arg; */
497 } else if(strnlen(value,5) < 5) {
498 printf("UNC name too short");
501 if (strnlen(value, 300) < 300) {
502 got_unc = 1;
503 if (strncmp(value, "//", 2) == 0) {
504 if(got_unc)
505 printf("unc name specified twice, ignoring second\n");
506 else
507 got_unc = 1;
508 } else if (strncmp(value, "\\\\", 2) != 0) {
509 printf("UNC Path does not begin with // or \\\\ \n");
510 return 1;
511 } else {
512 if(got_unc)
513 printf("unc name specified twice, ignoring second\n");
514 else
515 got_unc = 1;
517 } else {
518 printf("CIFS: UNC name too long\n");
519 return 1;
521 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
522 || (strncmp(data, "workg", 5) == 0)) {
523 /* note this allows for synonyms of "domain"
524 such as "DOM" and "dom" and "workgroup"
525 and "WORKGRP" etc. */
526 if (!value || !*value) {
527 printf("CIFS: invalid domain name\n");
528 return 1; /* needs_arg; */
530 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
531 got_domain = 1;
532 } else {
533 printf("domain name too long\n");
534 return 1;
536 } else if (strncmp(data, "cred", 4) == 0) {
537 if (value && *value) {
538 rc = open_cred_file(value);
539 if(rc) {
540 printf("error %d (%s) opening credential file %s\n",
541 rc, strerror(rc), value);
542 return 1;
544 } else {
545 printf("invalid credential file name specified\n");
546 return 1;
548 } else if (strncmp(data, "uid", 3) == 0) {
549 if (value && *value) {
550 got_uid = 1;
551 if (!isdigit(*value)) {
552 struct passwd *pw;
554 if (!(pw = getpwnam(value))) {
555 printf("bad user name \"%s\"\n", value);
556 exit(1);
558 snprintf(user, sizeof(user), "%u", pw->pw_uid);
559 } else {
560 strlcpy(user,value,sizeof(user));
563 goto nocopy;
564 } else if (strncmp(data, "gid", 3) == 0) {
565 if (value && *value) {
566 got_gid = 1;
567 if (!isdigit(*value)) {
568 struct group *gr;
570 if (!(gr = getgrnam(value))) {
571 printf("bad group name \"%s\"\n", value);
572 exit(1);
574 snprintf(group, sizeof(group), "%u", gr->gr_gid);
575 } else {
576 strlcpy(group,value,sizeof(group));
579 goto nocopy;
580 /* fmask and dmask synonyms for people used to smbfs syntax */
581 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
582 if (!value || !*value) {
583 printf ("Option '%s' requires a numerical argument\n", data);
584 return 1;
587 if (value[0] != '0') {
588 printf ("WARNING: '%s' not expressed in octal.\n", data);
591 if (strcmp (data, "fmask") == 0) {
592 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
593 data = "file_mode"; /* BB fix this */
595 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
596 if (!value || !*value) {
597 printf ("Option '%s' requires a numerical argument\n", data);
598 return 1;
601 if (value[0] != '0') {
602 printf ("WARNING: '%s' not expressed in octal.\n", data);
605 if (strcmp (data, "dmask") == 0) {
606 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
607 data = "dir_mode";
609 /* the following eight mount options should be
610 stripped out from what is passed into the kernel
611 since these eight options are best passed as the
612 mount flags rather than redundantly to the kernel
613 and could generate spurious warnings depending on the
614 level of the corresponding cifs vfs kernel code */
615 } else if (strncmp(data, "nosuid", 6) == 0) {
616 *filesys_flags |= MS_NOSUID;
617 } else if (strncmp(data, "suid", 4) == 0) {
618 *filesys_flags &= ~MS_NOSUID;
619 } else if (strncmp(data, "nodev", 5) == 0) {
620 *filesys_flags |= MS_NODEV;
621 } else if ((strncmp(data, "nobrl", 5) == 0) ||
622 (strncmp(data, "nolock", 6) == 0)) {
623 *filesys_flags &= ~MS_MANDLOCK;
624 } else if (strncmp(data, "dev", 3) == 0) {
625 *filesys_flags &= ~MS_NODEV;
626 } else if (strncmp(data, "noexec", 6) == 0) {
627 *filesys_flags |= MS_NOEXEC;
628 } else if (strncmp(data, "exec", 4) == 0) {
629 *filesys_flags &= ~MS_NOEXEC;
630 } else if (strncmp(data, "guest", 5) == 0) {
631 got_password=1;
632 } else if (strncmp(data, "ro", 2) == 0) {
633 *filesys_flags |= MS_RDONLY;
634 } else if (strncmp(data, "rw", 2) == 0) {
635 *filesys_flags &= ~MS_RDONLY;
636 } else if (strncmp(data, "remount", 7) == 0) {
637 *filesys_flags |= MS_REMOUNT;
638 } /* else if (strnicmp(data, "port", 4) == 0) {
639 if (value && *value) {
640 vol->port =
641 simple_strtoul(value, &value, 0);
643 } else if (strnicmp(data, "rsize", 5) == 0) {
644 if (value && *value) {
645 vol->rsize =
646 simple_strtoul(value, &value, 0);
648 } else if (strnicmp(data, "wsize", 5) == 0) {
649 if (value && *value) {
650 vol->wsize =
651 simple_strtoul(value, &value, 0);
653 } else if (strnicmp(data, "version", 3) == 0) {
654 } else {
655 printf("CIFS: Unknown mount option %s\n",data);
656 } */ /* nothing to do on those four mount options above.
657 Just pass to kernel and ignore them here */
659 /* Copy (possibly modified) option to out */
660 word_len = strlen(data);
661 if (value)
662 word_len += 1 + strlen(value);
664 out = (char *)realloc(out, out_len + word_len + 2);
665 if (out == NULL) {
666 perror("malloc");
667 exit(1);
670 if (out_len) {
671 strlcat(out, ",", out_len + word_len + 2);
672 out_len++;
675 if (value)
676 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
677 else
678 snprintf(out + out_len, word_len + 1, "%s", data);
679 out_len = strlen(out);
681 nocopy:
682 data = next_keyword;
685 /* special-case the uid and gid */
686 if (got_uid) {
687 word_len = strlen(user);
689 out = (char *)realloc(out, out_len + word_len + 6);
690 if (out == NULL) {
691 perror("malloc");
692 exit(1);
695 if (out_len) {
696 strlcat(out, ",", out_len + word_len + 6);
697 out_len++;
699 snprintf(out + out_len, word_len + 5, "uid=%s", user);
700 out_len = strlen(out);
702 if (got_gid) {
703 word_len = strlen(group);
705 out = (char *)realloc(out, out_len + 1 + word_len + 6);
706 if (out == NULL) {
707 perror("malloc");
708 exit(1);
711 if (out_len) {
712 strlcat(out, ",", out_len + word_len + 6);
713 out_len++;
715 snprintf(out + out_len, word_len + 5, "gid=%s", group);
716 out_len = strlen(out);
719 SAFE_FREE(*optionsp);
720 *optionsp = out;
721 return 0;
724 /* replace all (one or more) commas with double commas */
725 static void check_for_comma(char ** ppasswrd)
727 char *new_pass_buf;
728 char *pass;
729 int i,j;
730 int number_of_commas = 0;
731 int len;
733 if(ppasswrd == NULL)
734 return;
735 else
736 (pass = *ppasswrd);
738 len = strlen(pass);
740 for(i=0;i<len;i++) {
741 if(pass[i] == ',')
742 number_of_commas++;
745 if(number_of_commas == 0)
746 return;
747 if(number_of_commas > MOUNT_PASSWD_SIZE) {
748 /* would otherwise overflow the mount options buffer */
749 printf("\nInvalid password. Password contains too many commas.\n");
750 return;
753 new_pass_buf = (char *)malloc(len+number_of_commas+1);
754 if(new_pass_buf == NULL)
755 return;
757 for(i=0,j=0;i<len;i++,j++) {
758 new_pass_buf[j] = pass[i];
759 if(pass[i] == ',') {
760 j++;
761 new_pass_buf[j] = pass[i];
764 new_pass_buf[len+number_of_commas] = 0;
766 SAFE_FREE(*ppasswrd);
767 *ppasswrd = new_pass_buf;
769 return;
772 /* Usernames can not have backslash in them and we use
773 [BB check if usernames can have forward slash in them BB]
774 backslash as domain\user separator character
776 static char * check_for_domain(char **ppuser)
778 char * original_string;
779 char * usernm;
780 char * domainnm;
781 int original_len;
782 int len;
783 int i;
785 if(ppuser == NULL)
786 return NULL;
788 original_string = *ppuser;
790 if (original_string == NULL)
791 return NULL;
793 original_len = strlen(original_string);
795 usernm = strchr(*ppuser,'/');
796 if (usernm == NULL) {
797 usernm = strchr(*ppuser,'\\');
798 if (usernm == NULL)
799 return NULL;
802 if(got_domain) {
803 printf("Domain name specified twice. Username probably malformed\n");
804 return NULL;
807 usernm[0] = 0;
808 domainnm = *ppuser;
809 if (domainnm[0] != 0) {
810 got_domain = 1;
811 } else {
812 printf("null domain\n");
814 len = strlen(domainnm);
815 /* reset domainm to new buffer, and copy
816 domain name into it */
817 domainnm = (char *)malloc(len+1);
818 if(domainnm == NULL)
819 return NULL;
821 strlcpy(domainnm,*ppuser,len+1);
823 /* move_string(*ppuser, usernm+1) */
824 len = strlen(usernm+1);
826 if(len >= original_len) {
827 /* should not happen */
828 return domainnm;
831 for(i=0;i<original_len;i++) {
832 if(i<len)
833 original_string[i] = usernm[i+1];
834 else /* stuff with commas to remove last parm */
835 original_string[i] = ',';
838 /* BB add check for more than one slash?
839 strchr(*ppuser,'/');
840 strchr(*ppuser,'\\')
843 return domainnm;
846 /* replace all occurances of "from" in a string with "to" */
847 static void replace_char(char *string, char from, char to, int maxlen)
849 char *lastchar = string + maxlen;
850 while (string) {
851 string = strchr(string, from);
852 if (string) {
853 *string = to;
854 if (string >= lastchar)
855 return;
860 /* Note that caller frees the returned buffer if necessary */
861 static char * parse_server(char ** punc_name)
863 char * unc_name = *punc_name;
864 int length = strnlen(unc_name, MAX_UNC_LEN);
865 char * share;
866 char * ipaddress_string = NULL;
867 struct hostent * host_entry = NULL;
868 struct in_addr server_ipaddr;
870 if(length > (MAX_UNC_LEN - 1)) {
871 printf("mount error: UNC name too long");
872 return NULL;
874 if (strncasecmp("cifs://",unc_name,7) == 0)
875 return parse_cifs_url(unc_name+7);
876 if (strncasecmp("smb://",unc_name,6) == 0) {
877 return parse_cifs_url(unc_name+6);
880 if(length < 3) {
881 /* BB add code to find DFS root here */
882 printf("\nMounting the DFS root for domain not implemented yet\n");
883 return NULL;
884 } else {
885 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
886 /* check for nfs syntax ie server:share */
887 share = strchr(unc_name,':');
888 if(share) {
889 *punc_name = (char *)malloc(length+3);
890 if(*punc_name == NULL) {
891 /* put the original string back if
892 no memory left */
893 *punc_name = unc_name;
894 return NULL;
896 *share = '/';
897 strlcpy((*punc_name)+2,unc_name,length+1);
898 SAFE_FREE(unc_name);
899 unc_name = *punc_name;
900 unc_name[length+2] = 0;
901 goto continue_unc_parsing;
902 } else {
903 printf("mount error: improperly formatted UNC name.");
904 printf(" %s does not begin with \\\\ or //\n",unc_name);
905 return NULL;
907 } else {
908 continue_unc_parsing:
909 unc_name[0] = '/';
910 unc_name[1] = '/';
911 unc_name += 2;
913 /* allow for either delimiter between host and sharename */
914 if ((share = strpbrk(unc_name, "/\\"))) {
915 *share = 0; /* temporarily terminate the string */
916 share += 1;
917 if(got_ip == 0) {
918 host_entry = gethostbyname(unc_name);
920 *(share - 1) = '/'; /* put delimiter back */
922 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
923 if ((prefixpath = strpbrk(share, "/\\"))) {
924 *prefixpath = 0; /* permanently terminate the string */
925 if (!strlen(++prefixpath))
926 prefixpath = NULL; /* this needs to be done explicitly */
928 if(got_ip) {
929 if(verboseflag)
930 printf("ip address specified explicitly\n");
931 return NULL;
933 if(host_entry == NULL) {
934 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
935 return NULL;
936 } else {
937 /* BB should we pass an alternate version of the share name as Unicode */
938 /* BB what about ipv6? BB */
939 /* BB add retries with alternate servers in list */
941 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
943 ipaddress_string = inet_ntoa(server_ipaddr);
944 if(ipaddress_string == NULL) {
945 printf("mount error: could not get valid ip address for target server\n");
946 return NULL;
948 return ipaddress_string;
950 } else {
951 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
952 printf("Mounting the DFS root for a particular server not implemented yet\n");
953 return NULL;
959 static struct option longopts[] = {
960 { "all", 0, NULL, 'a' },
961 { "help",0, NULL, 'h' },
962 { "move",0, NULL, 'm' },
963 { "bind",0, NULL, 'b' },
964 { "read-only", 0, NULL, 'r' },
965 { "ro", 0, NULL, 'r' },
966 { "verbose", 0, NULL, 'v' },
967 { "version", 0, NULL, 'V' },
968 { "read-write", 0, NULL, 'w' },
969 { "rw", 0, NULL, 'w' },
970 { "options", 1, NULL, 'o' },
971 { "type", 1, NULL, 't' },
972 { "rsize",1, NULL, 'R' },
973 { "wsize",1, NULL, 'W' },
974 { "uid", 1, NULL, '1'},
975 { "gid", 1, NULL, '2'},
976 { "user",1,NULL,'u'},
977 { "username",1,NULL,'u'},
978 { "dom",1,NULL,'d'},
979 { "domain",1,NULL,'d'},
980 { "password",1,NULL,'p'},
981 { "pass",1,NULL,'p'},
982 { "credentials",1,NULL,'c'},
983 { "port",1,NULL,'P'},
984 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
985 { NULL, 0, NULL, 0 }
988 /* convert a string to uppercase. return false if the string
989 * wasn't ASCII or was a NULL ptr */
990 static int
991 uppercase_string(char *string)
993 if (!string)
994 return 0;
996 while (*string) {
997 /* check for unicode */
998 if ((unsigned char) string[0] & 0x80)
999 return 0;
1000 *string = toupper((unsigned char) *string);
1001 string++;
1004 return 1;
1007 int main(int argc, char ** argv)
1009 int c;
1010 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1011 char * orgoptions = NULL;
1012 char * share_name = NULL;
1013 char * ipaddr = NULL;
1014 char * uuid = NULL;
1015 char * mountpoint = NULL;
1016 char * options = NULL;
1017 char * resolved_path = NULL;
1018 char * temp;
1019 char * dev_name;
1020 int rc;
1021 int rsize = 0;
1022 int wsize = 0;
1023 int nomtab = 0;
1024 int uid = 0;
1025 int gid = 0;
1026 int optlen = 0;
1027 int orgoptlen = 0;
1028 size_t options_size = 0;
1029 int retry = 0; /* set when we have to retry mount with uppercase */
1030 struct stat statbuf;
1031 struct utsname sysinfo;
1032 struct mntent mountent;
1033 FILE * pmntfile;
1035 /* setlocale(LC_ALL, "");
1036 bindtextdomain(PACKAGE, LOCALEDIR);
1037 textdomain(PACKAGE); */
1039 if(argc && argv) {
1040 thisprogram = argv[0];
1041 } else {
1042 mount_cifs_usage();
1043 exit(1);
1046 if(thisprogram == NULL)
1047 thisprogram = "mount.cifs";
1049 uname(&sysinfo);
1050 /* BB add workstation name and domain and pass down */
1052 /* #ifdef _GNU_SOURCE
1053 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1054 #endif */
1055 if(argc > 2) {
1056 dev_name = argv[1];
1057 share_name = strndup(argv[1], MAX_UNC_LEN);
1058 if (share_name == NULL) {
1059 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1060 exit(1);
1062 mountpoint = argv[2];
1063 } else {
1064 mount_cifs_usage();
1065 exit(1);
1068 /* add sharename in opts string as unc= parm */
1070 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1071 longopts, NULL)) != -1) {
1072 switch (c) {
1073 /* No code to do the following options yet */
1074 /* case 'l':
1075 list_with_volumelabel = 1;
1076 break;
1077 case 'L':
1078 volumelabel = optarg;
1079 break; */
1080 /* case 'a':
1081 ++mount_all;
1082 break; */
1084 case '?':
1085 case 'h': /* help */
1086 mount_cifs_usage ();
1087 exit(1);
1088 case 'n':
1089 ++nomtab;
1090 break;
1091 case 'b':
1092 #ifdef MS_BIND
1093 flags |= MS_BIND;
1094 #else
1095 fprintf(stderr,
1096 "option 'b' (MS_BIND) not supported\n");
1097 #endif
1098 break;
1099 case 'm':
1100 #ifdef MS_MOVE
1101 flags |= MS_MOVE;
1102 #else
1103 fprintf(stderr,
1104 "option 'm' (MS_MOVE) not supported\n");
1105 #endif
1106 break;
1107 case 'o':
1108 orgoptions = strdup(optarg);
1109 break;
1110 case 'r': /* mount readonly */
1111 flags |= MS_RDONLY;
1112 break;
1113 case 'U':
1114 uuid = optarg;
1115 break;
1116 case 'v':
1117 ++verboseflag;
1118 break;
1119 case 'V':
1120 printf ("mount.cifs version: %s.%s%s\n",
1121 MOUNT_CIFS_VERSION_MAJOR,
1122 MOUNT_CIFS_VERSION_MINOR,
1123 MOUNT_CIFS_VENDOR_SUFFIX);
1124 exit (0);
1125 case 'w':
1126 flags &= ~MS_RDONLY;
1127 break;
1128 case 'R':
1129 rsize = atoi(optarg) ;
1130 break;
1131 case 'W':
1132 wsize = atoi(optarg);
1133 break;
1134 case '1':
1135 if (isdigit(*optarg)) {
1136 char *ep;
1138 uid = strtoul(optarg, &ep, 10);
1139 if (*ep) {
1140 printf("bad uid value \"%s\"\n", optarg);
1141 exit(1);
1143 } else {
1144 struct passwd *pw;
1146 if (!(pw = getpwnam(optarg))) {
1147 printf("bad user name \"%s\"\n", optarg);
1148 exit(1);
1150 uid = pw->pw_uid;
1151 endpwent();
1153 break;
1154 case '2':
1155 if (isdigit(*optarg)) {
1156 char *ep;
1158 gid = strtoul(optarg, &ep, 10);
1159 if (*ep) {
1160 printf("bad gid value \"%s\"\n", optarg);
1161 exit(1);
1163 } else {
1164 struct group *gr;
1166 if (!(gr = getgrnam(optarg))) {
1167 printf("bad user name \"%s\"\n", optarg);
1168 exit(1);
1170 gid = gr->gr_gid;
1171 endpwent();
1173 break;
1174 case 'u':
1175 got_user = 1;
1176 user_name = optarg;
1177 break;
1178 case 'd':
1179 domain_name = optarg; /* BB fix this - currently ignored */
1180 got_domain = 1;
1181 break;
1182 case 'p':
1183 if(mountpassword == NULL)
1184 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1185 if(mountpassword) {
1186 got_password = 1;
1187 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1189 break;
1190 case 'S':
1191 get_password_from_file(0 /* stdin */,NULL);
1192 break;
1193 case 't':
1194 break;
1195 default:
1196 printf("unknown mount option %c\n",c);
1197 mount_cifs_usage();
1198 exit(1);
1202 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1203 mount_cifs_usage();
1204 exit(1);
1207 if (getenv("PASSWD")) {
1208 if(mountpassword == NULL)
1209 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1210 if(mountpassword) {
1211 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1212 got_password = 1;
1214 } else if (getenv("PASSWD_FD")) {
1215 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1216 } else if (getenv("PASSWD_FILE")) {
1217 get_password_from_file(0, getenv("PASSWD_FILE"));
1220 if (orgoptions && parse_options(&orgoptions, &flags)) {
1221 rc = -1;
1222 goto mount_exit;
1224 ipaddr = parse_server(&share_name);
1225 if((ipaddr == NULL) && (got_ip == 0)) {
1226 printf("No ip address specified and hostname not found\n");
1227 rc = -1;
1228 goto mount_exit;
1231 /* BB save off path and pop after mount returns? */
1232 resolved_path = (char *)malloc(PATH_MAX+1);
1233 if(resolved_path) {
1234 /* Note that if we can not canonicalize the name, we get
1235 another chance to see if it is valid when we chdir to it */
1236 if (realpath(mountpoint, resolved_path)) {
1237 mountpoint = resolved_path;
1240 if(chdir(mountpoint)) {
1241 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1242 rc = -1;
1243 goto mount_exit;
1246 if(stat (".", &statbuf)) {
1247 printf("mount error: mount point %s does not exist\n",mountpoint);
1248 rc = -1;
1249 goto mount_exit;
1252 if (S_ISDIR(statbuf.st_mode) == 0) {
1253 printf("mount error: mount point %s is not a directory\n",mountpoint);
1254 rc = -1;
1255 goto mount_exit;
1258 if((getuid() != 0) && (geteuid() == 0)) {
1259 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1260 #ifndef CIFS_ALLOW_USR_SUID
1261 /* Do not allow user mounts to control suid flag
1262 for mount unless explicitly built that way */
1263 flags |= MS_NOSUID | MS_NODEV;
1264 #endif
1265 } else {
1266 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1267 return -1;
1271 if(got_user == 0) {
1272 user_name = getusername();
1273 got_user = 1;
1276 if(got_password == 0) {
1277 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1278 no good replacement yet. */
1279 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1280 if (!tmp_pass || !mountpassword) {
1281 printf("Password not entered, exiting\n");
1282 return -1;
1284 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1285 got_password = 1;
1287 /* FIXME launch daemon (handles dfs name resolution and credential change)
1288 remember to clear parms and overwrite password field before launching */
1289 mount_retry:
1290 if(orgoptions) {
1291 optlen = strlen(orgoptions);
1292 orgoptlen = optlen;
1293 } else
1294 optlen = 0;
1295 if(share_name)
1296 optlen += strlen(share_name) + 4;
1297 else {
1298 printf("No server share name specified\n");
1299 printf("\nMounting the DFS root for server not implemented yet\n");
1300 exit(1);
1302 if(user_name)
1303 optlen += strlen(user_name) + 6;
1304 if(ipaddr)
1305 optlen += strlen(ipaddr) + 4;
1306 if(mountpassword)
1307 optlen += strlen(mountpassword) + 6;
1308 SAFE_FREE(options);
1309 options_size = optlen + 10 + DOMAIN_SIZE;
1310 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 */);
1312 if(options == NULL) {
1313 printf("Could not allocate memory for mount options\n");
1314 return -1;
1317 options[0] = 0;
1318 strlcpy(options,"unc=",options_size);
1319 strlcat(options,share_name,options_size);
1320 /* scan backwards and reverse direction of slash */
1321 temp = strrchr(options, '/');
1322 if(temp > options + 6)
1323 *temp = '\\';
1324 if(ipaddr) {
1325 strlcat(options,",ip=",options_size);
1326 strlcat(options,ipaddr,options_size);
1329 if(user_name) {
1330 /* check for syntax like user=domain\user */
1331 if(got_domain == 0)
1332 domain_name = check_for_domain(&user_name);
1333 strlcat(options,",user=",options_size);
1334 strlcat(options,user_name,options_size);
1336 if(retry == 0) {
1337 if(domain_name) {
1338 /* extra length accounted for in option string above */
1339 strlcat(options,",domain=",options_size);
1340 strlcat(options,domain_name,options_size);
1343 if(mountpassword) {
1344 /* Commas have to be doubled, or else they will
1345 look like the parameter separator */
1346 /* if(sep is not set)*/
1347 if(retry == 0)
1348 check_for_comma(&mountpassword);
1349 strlcat(options,",pass=",options_size);
1350 strlcat(options,mountpassword,options_size);
1353 strlcat(options,",ver=",options_size);
1354 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1356 if(orgoptions) {
1357 strlcat(options,",",options_size);
1358 strlcat(options,orgoptions,options_size);
1360 if(prefixpath) {
1361 strlcat(options,",prefixpath=",options_size);
1362 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1364 if(verboseflag)
1365 printf("\nmount.cifs kernel mount options %s \n",options);
1367 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1368 replace_char(dev_name, '\\', '/', strlen(share_name));
1370 if(mount(dev_name, mountpoint, "cifs", flags, options)) {
1371 /* remember to kill daemon on error */
1372 switch (errno) {
1373 case 0:
1374 printf("mount failed but no error number set\n");
1375 break;
1376 case ENODEV:
1377 printf("mount error: cifs filesystem not supported by the system\n");
1378 break;
1379 case ENXIO:
1380 if(retry == 0) {
1381 retry = 1;
1382 if (uppercase_string(dev_name) &&
1383 uppercase_string(share_name) &&
1384 uppercase_string(prefixpath)) {
1385 printf("retrying with upper case share name\n");
1386 goto mount_retry;
1389 default:
1390 printf("mount error %d = %s\n",errno,strerror(errno));
1392 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1393 rc = -1;
1394 goto mount_exit;
1395 } else {
1396 pmntfile = setmntent(MOUNTED, "a+");
1397 if(pmntfile) {
1398 mountent.mnt_fsname = dev_name;
1399 mountent.mnt_dir = mountpoint;
1400 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1401 mountent.mnt_opts = (char *)malloc(220);
1402 if(mountent.mnt_opts) {
1403 char * mount_user = getusername();
1404 memset(mountent.mnt_opts,0,200);
1405 if(flags & MS_RDONLY)
1406 strlcat(mountent.mnt_opts,"ro",220);
1407 else
1408 strlcat(mountent.mnt_opts,"rw",220);
1409 if(flags & MS_MANDLOCK)
1410 strlcat(mountent.mnt_opts,",mand",220);
1411 if(flags & MS_NOEXEC)
1412 strlcat(mountent.mnt_opts,",noexec",220);
1413 if(flags & MS_NOSUID)
1414 strlcat(mountent.mnt_opts,",nosuid",220);
1415 if(flags & MS_NODEV)
1416 strlcat(mountent.mnt_opts,",nodev",220);
1417 if(flags & MS_SYNCHRONOUS)
1418 strlcat(mountent.mnt_opts,",synch",220);
1419 if(mount_user) {
1420 if(getuid() != 0) {
1421 strlcat(mountent.mnt_opts,",user=",220);
1422 strlcat(mountent.mnt_opts,mount_user,220);
1424 /* free(mount_user); do not free static mem */
1427 mountent.mnt_freq = 0;
1428 mountent.mnt_passno = 0;
1429 rc = addmntent(pmntfile,&mountent);
1430 endmntent(pmntfile);
1431 SAFE_FREE(mountent.mnt_opts);
1432 } else {
1433 printf("could not update mount table\n");
1436 rc = 0;
1437 mount_exit:
1438 if(mountpassword) {
1439 int len = strlen(mountpassword);
1440 memset(mountpassword,0,len);
1441 SAFE_FREE(mountpassword);
1444 SAFE_FREE(options);
1445 SAFE_FREE(orgoptions);
1446 SAFE_FREE(resolved_path);
1447 SAFE_FREE(share_name);
1448 return rc;