netapi: implement NetLocalGroupAddMembers_r().
[Samba.git] / source / client / mount.cifs.c
blobc7009e306c08422245e1a925b68bb0fbeb3f6294
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, "dom" /* domain */, 3) == 0)
521 || (strncmp(data, "workg", 5) == 0)) {
522 /* note this allows for synonyms of "domain"
523 such as "DOM" and "dom" and "workgroup"
524 and "WORKGRP" etc. */
525 if (!value || !*value) {
526 printf("CIFS: invalid domain name\n");
527 return 1; /* needs_arg; */
529 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
530 got_domain = 1;
531 } else {
532 printf("domain name too long\n");
533 return 1;
535 } else if (strncmp(data, "cred", 4) == 0) {
536 if (value && *value) {
537 rc = open_cred_file(value);
538 if(rc) {
539 printf("error %d opening credential file %s\n",rc, value);
540 return 1;
542 } else {
543 printf("invalid credential file name specified\n");
544 return 1;
546 } else if (strncmp(data, "uid", 3) == 0) {
547 if (value && *value) {
548 got_uid = 1;
549 if (!isdigit(*value)) {
550 struct passwd *pw;
552 if (!(pw = getpwnam(value))) {
553 printf("bad user name \"%s\"\n", value);
554 exit(1);
556 snprintf(user, sizeof(user), "%u", pw->pw_uid);
557 } else {
558 strlcpy(user,value,sizeof(user));
561 goto nocopy;
562 } else if (strncmp(data, "gid", 3) == 0) {
563 if (value && *value) {
564 got_gid = 1;
565 if (!isdigit(*value)) {
566 struct group *gr;
568 if (!(gr = getgrnam(value))) {
569 printf("bad group name \"%s\"\n", value);
570 exit(1);
572 snprintf(group, sizeof(group), "%u", gr->gr_gid);
573 } else {
574 strlcpy(group,value,sizeof(group));
577 goto nocopy;
578 /* fmask and dmask synonyms for people used to smbfs syntax */
579 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
580 if (!value || !*value) {
581 printf ("Option '%s' requires a numerical argument\n", data);
582 return 1;
585 if (value[0] != '0') {
586 printf ("WARNING: '%s' not expressed in octal.\n", data);
589 if (strcmp (data, "fmask") == 0) {
590 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
591 data = "file_mode"; /* BB fix this */
593 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
594 if (!value || !*value) {
595 printf ("Option '%s' requires a numerical argument\n", data);
596 return 1;
599 if (value[0] != '0') {
600 printf ("WARNING: '%s' not expressed in octal.\n", data);
603 if (strcmp (data, "dmask") == 0) {
604 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
605 data = "dir_mode";
607 /* the following eight mount options should be
608 stripped out from what is passed into the kernel
609 since these eight options are best passed as the
610 mount flags rather than redundantly to the kernel
611 and could generate spurious warnings depending on the
612 level of the corresponding cifs vfs kernel code */
613 } else if (strncmp(data, "nosuid", 6) == 0) {
614 *filesys_flags |= MS_NOSUID;
615 } else if (strncmp(data, "suid", 4) == 0) {
616 *filesys_flags &= ~MS_NOSUID;
617 } else if (strncmp(data, "nodev", 5) == 0) {
618 *filesys_flags |= MS_NODEV;
619 } else if ((strncmp(data, "nobrl", 5) == 0) ||
620 (strncmp(data, "nolock", 6) == 0)) {
621 *filesys_flags &= ~MS_MANDLOCK;
622 } else if (strncmp(data, "dev", 3) == 0) {
623 *filesys_flags &= ~MS_NODEV;
624 } else if (strncmp(data, "noexec", 6) == 0) {
625 *filesys_flags |= MS_NOEXEC;
626 } else if (strncmp(data, "exec", 4) == 0) {
627 *filesys_flags &= ~MS_NOEXEC;
628 } else if (strncmp(data, "guest", 5) == 0) {
629 got_password=1;
630 } else if (strncmp(data, "ro", 2) == 0) {
631 *filesys_flags |= MS_RDONLY;
632 } else if (strncmp(data, "rw", 2) == 0) {
633 *filesys_flags &= ~MS_RDONLY;
634 } else if (strncmp(data, "remount", 7) == 0) {
635 *filesys_flags |= MS_REMOUNT;
636 } /* else if (strnicmp(data, "port", 4) == 0) {
637 if (value && *value) {
638 vol->port =
639 simple_strtoul(value, &value, 0);
641 } else if (strnicmp(data, "rsize", 5) == 0) {
642 if (value && *value) {
643 vol->rsize =
644 simple_strtoul(value, &value, 0);
646 } else if (strnicmp(data, "wsize", 5) == 0) {
647 if (value && *value) {
648 vol->wsize =
649 simple_strtoul(value, &value, 0);
651 } else if (strnicmp(data, "version", 3) == 0) {
652 } else {
653 printf("CIFS: Unknown mount option %s\n",data);
654 } */ /* nothing to do on those four mount options above.
655 Just pass to kernel and ignore them here */
657 /* Copy (possibly modified) option to out */
658 word_len = strlen(data);
659 if (value)
660 word_len += 1 + strlen(value);
662 out = (char *)realloc(out, out_len + word_len + 2);
663 if (out == NULL) {
664 perror("malloc");
665 exit(1);
668 if (out_len) {
669 strlcat(out, ",", out_len + word_len + 2);
670 out_len++;
673 if (value)
674 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
675 else
676 snprintf(out + out_len, word_len + 1, "%s", data);
677 out_len = strlen(out);
679 nocopy:
680 data = next_keyword;
683 /* special-case the uid and gid */
684 if (got_uid) {
685 word_len = strlen(user);
687 out = (char *)realloc(out, out_len + word_len + 6);
688 if (out == NULL) {
689 perror("malloc");
690 exit(1);
693 if (out_len) {
694 strlcat(out, ",", out_len + word_len + 6);
695 out_len++;
697 snprintf(out + out_len, word_len + 5, "uid=%s", user);
698 out_len = strlen(out);
700 if (got_gid) {
701 word_len = strlen(group);
703 out = (char *)realloc(out, out_len + 1 + word_len + 6);
704 if (out == NULL) {
705 perror("malloc");
706 exit(1);
709 if (out_len) {
710 strlcat(out, ",", out_len + word_len + 6);
711 out_len++;
713 snprintf(out + out_len, word_len + 5, "gid=%s", group);
714 out_len = strlen(out);
717 SAFE_FREE(*optionsp);
718 *optionsp = out;
719 return 0;
722 /* replace all (one or more) commas with double commas */
723 static void check_for_comma(char ** ppasswrd)
725 char *new_pass_buf;
726 char *pass;
727 int i,j;
728 int number_of_commas = 0;
729 int len;
731 if(ppasswrd == NULL)
732 return;
733 else
734 (pass = *ppasswrd);
736 len = strlen(pass);
738 for(i=0;i<len;i++) {
739 if(pass[i] == ',')
740 number_of_commas++;
743 if(number_of_commas == 0)
744 return;
745 if(number_of_commas > MOUNT_PASSWD_SIZE) {
746 /* would otherwise overflow the mount options buffer */
747 printf("\nInvalid password. Password contains too many commas.\n");
748 return;
751 new_pass_buf = (char *)malloc(len+number_of_commas+1);
752 if(new_pass_buf == NULL)
753 return;
755 for(i=0,j=0;i<len;i++,j++) {
756 new_pass_buf[j] = pass[i];
757 if(pass[i] == ',') {
758 j++;
759 new_pass_buf[j] = pass[i];
762 new_pass_buf[len+number_of_commas] = 0;
764 SAFE_FREE(*ppasswrd);
765 *ppasswrd = new_pass_buf;
767 return;
770 /* Usernames can not have backslash in them and we use
771 [BB check if usernames can have forward slash in them BB]
772 backslash as domain\user separator character
774 static char * check_for_domain(char **ppuser)
776 char * original_string;
777 char * usernm;
778 char * domainnm;
779 int original_len;
780 int len;
781 int i;
783 if(ppuser == NULL)
784 return NULL;
786 original_string = *ppuser;
788 if (original_string == NULL)
789 return NULL;
791 original_len = strlen(original_string);
793 usernm = strchr(*ppuser,'/');
794 if (usernm == NULL) {
795 usernm = strchr(*ppuser,'\\');
796 if (usernm == NULL)
797 return NULL;
800 if(got_domain) {
801 printf("Domain name specified twice. Username probably malformed\n");
802 return NULL;
805 usernm[0] = 0;
806 domainnm = *ppuser;
807 if (domainnm[0] != 0) {
808 got_domain = 1;
809 } else {
810 printf("null domain\n");
812 len = strlen(domainnm);
813 /* reset domainm to new buffer, and copy
814 domain name into it */
815 domainnm = (char *)malloc(len+1);
816 if(domainnm == NULL)
817 return NULL;
819 strlcpy(domainnm,*ppuser,len+1);
821 /* move_string(*ppuser, usernm+1) */
822 len = strlen(usernm+1);
824 if(len >= original_len) {
825 /* should not happen */
826 return domainnm;
829 for(i=0;i<original_len;i++) {
830 if(i<len)
831 original_string[i] = usernm[i+1];
832 else /* stuff with commas to remove last parm */
833 original_string[i] = ',';
836 /* BB add check for more than one slash?
837 strchr(*ppuser,'/');
838 strchr(*ppuser,'\\')
841 return domainnm;
844 /* replace all occurances of "from" in a string with "to" */
845 static void replace_char(char *string, char from, char to, int maxlen)
847 char *lastchar = string + maxlen;
848 while (string) {
849 string = strchr(string, from);
850 if (string) {
851 *string = to;
852 if (string >= lastchar)
853 return;
858 /* Note that caller frees the returned buffer if necessary */
859 static char * parse_server(char ** punc_name)
861 char * unc_name = *punc_name;
862 int length = strnlen(unc_name, MAX_UNC_LEN);
863 char * share;
864 char * ipaddress_string = NULL;
865 struct hostent * host_entry = NULL;
866 struct in_addr server_ipaddr;
868 if(length > (MAX_UNC_LEN - 1)) {
869 printf("mount error: UNC name too long");
870 return NULL;
872 if (strncasecmp("cifs://",unc_name,7) == 0)
873 return parse_cifs_url(unc_name+7);
874 if (strncasecmp("smb://",unc_name,6) == 0) {
875 return parse_cifs_url(unc_name+6);
878 if(length < 3) {
879 /* BB add code to find DFS root here */
880 printf("\nMounting the DFS root for domain not implemented yet\n");
881 return NULL;
882 } else {
883 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
884 /* check for nfs syntax ie server:share */
885 share = strchr(unc_name,':');
886 if(share) {
887 *punc_name = (char *)malloc(length+3);
888 if(*punc_name == NULL) {
889 /* put the original string back if
890 no memory left */
891 *punc_name = unc_name;
892 return NULL;
894 *share = '/';
895 strlcpy((*punc_name)+2,unc_name,length+1);
896 SAFE_FREE(unc_name);
897 unc_name = *punc_name;
898 unc_name[length+2] = 0;
899 goto continue_unc_parsing;
900 } else {
901 printf("mount error: improperly formatted UNC name.");
902 printf(" %s does not begin with \\\\ or //\n",unc_name);
903 return NULL;
905 } else {
906 continue_unc_parsing:
907 unc_name[0] = '/';
908 unc_name[1] = '/';
909 unc_name += 2;
911 /* allow for either delimiter between host and sharename */
912 if ((share = strpbrk(unc_name, "/\\"))) {
913 *share = 0; /* temporarily terminate the string */
914 share += 1;
915 if(got_ip == 0) {
916 host_entry = gethostbyname(unc_name);
918 *(share - 1) = '/'; /* put delimiter back */
920 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
921 if ((prefixpath = strpbrk(share, "/\\"))) {
922 *prefixpath = 0; /* permanently terminate the string */
923 if (!strlen(++prefixpath))
924 prefixpath = NULL; /* this needs to be done explicitly */
926 if(got_ip) {
927 if(verboseflag)
928 printf("ip address specified explicitly\n");
929 return NULL;
931 if(host_entry == NULL) {
932 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
933 return NULL;
934 } else {
935 /* BB should we pass an alternate version of the share name as Unicode */
936 /* BB what about ipv6? BB */
937 /* BB add retries with alternate servers in list */
939 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
941 ipaddress_string = inet_ntoa(server_ipaddr);
942 if(ipaddress_string == NULL) {
943 printf("mount error: could not get valid ip address for target server\n");
944 return NULL;
946 return ipaddress_string;
948 } else {
949 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
950 printf("Mounting the DFS root for a particular server not implemented yet\n");
951 return NULL;
957 static struct option longopts[] = {
958 { "all", 0, NULL, 'a' },
959 { "help",0, NULL, 'h' },
960 { "move",0, NULL, 'm' },
961 { "bind",0, NULL, 'b' },
962 { "read-only", 0, NULL, 'r' },
963 { "ro", 0, NULL, 'r' },
964 { "verbose", 0, NULL, 'v' },
965 { "version", 0, NULL, 'V' },
966 { "read-write", 0, NULL, 'w' },
967 { "rw", 0, NULL, 'w' },
968 { "options", 1, NULL, 'o' },
969 { "type", 1, NULL, 't' },
970 { "rsize",1, NULL, 'R' },
971 { "wsize",1, NULL, 'W' },
972 { "uid", 1, NULL, '1'},
973 { "gid", 1, NULL, '2'},
974 { "user",1,NULL,'u'},
975 { "username",1,NULL,'u'},
976 { "dom",1,NULL,'d'},
977 { "domain",1,NULL,'d'},
978 { "password",1,NULL,'p'},
979 { "pass",1,NULL,'p'},
980 { "credentials",1,NULL,'c'},
981 { "port",1,NULL,'P'},
982 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
983 { NULL, 0, NULL, 0 }
986 /* convert a string to uppercase. return false if the string
987 * wasn't ASCII or was a NULL ptr */
988 static int
989 uppercase_string(char *string)
991 if (!string)
992 return 0;
994 while (*string) {
995 /* check for unicode */
996 if ((unsigned char) string[0] & 0x80)
997 return 0;
998 *string = toupper((unsigned char) *string);
999 string++;
1002 return 1;
1005 int main(int argc, char ** argv)
1007 int c;
1008 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1009 char * orgoptions = NULL;
1010 char * share_name = NULL;
1011 char * ipaddr = NULL;
1012 char * uuid = NULL;
1013 char * mountpoint = NULL;
1014 char * options = NULL;
1015 char * resolved_path = NULL;
1016 char * temp;
1017 char * dev_name;
1018 int rc;
1019 int rsize = 0;
1020 int wsize = 0;
1021 int nomtab = 0;
1022 int uid = 0;
1023 int gid = 0;
1024 int optlen = 0;
1025 int orgoptlen = 0;
1026 size_t options_size = 0;
1027 int retry = 0; /* set when we have to retry mount with uppercase */
1028 struct stat statbuf;
1029 struct utsname sysinfo;
1030 struct mntent mountent;
1031 FILE * pmntfile;
1033 /* setlocale(LC_ALL, "");
1034 bindtextdomain(PACKAGE, LOCALEDIR);
1035 textdomain(PACKAGE); */
1037 if(argc && argv) {
1038 thisprogram = argv[0];
1039 } else {
1040 mount_cifs_usage();
1041 exit(1);
1044 if(thisprogram == NULL)
1045 thisprogram = "mount.cifs";
1047 uname(&sysinfo);
1048 /* BB add workstation name and domain and pass down */
1050 /* #ifdef _GNU_SOURCE
1051 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1052 #endif */
1053 if(argc > 2) {
1054 dev_name = argv[1];
1055 share_name = strndup(argv[1], MAX_UNC_LEN);
1056 if (share_name == NULL) {
1057 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1058 exit(1);
1060 mountpoint = argv[2];
1061 } else {
1062 mount_cifs_usage();
1063 exit(1);
1066 /* add sharename in opts string as unc= parm */
1068 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1069 longopts, NULL)) != -1) {
1070 switch (c) {
1071 /* No code to do the following options yet */
1072 /* case 'l':
1073 list_with_volumelabel = 1;
1074 break;
1075 case 'L':
1076 volumelabel = optarg;
1077 break; */
1078 /* case 'a':
1079 ++mount_all;
1080 break; */
1082 case '?':
1083 case 'h': /* help */
1084 mount_cifs_usage ();
1085 exit(1);
1086 case 'n':
1087 ++nomtab;
1088 break;
1089 case 'b':
1090 #ifdef MS_BIND
1091 flags |= MS_BIND;
1092 #else
1093 fprintf(stderr,
1094 "option 'b' (MS_BIND) not supported\n");
1095 #endif
1096 break;
1097 case 'm':
1098 #ifdef MS_MOVE
1099 flags |= MS_MOVE;
1100 #else
1101 fprintf(stderr,
1102 "option 'm' (MS_MOVE) not supported\n");
1103 #endif
1104 break;
1105 case 'o':
1106 orgoptions = strdup(optarg);
1107 break;
1108 case 'r': /* mount readonly */
1109 flags |= MS_RDONLY;
1110 break;
1111 case 'U':
1112 uuid = optarg;
1113 break;
1114 case 'v':
1115 ++verboseflag;
1116 break;
1117 case 'V':
1118 printf ("mount.cifs version: %s.%s%s\n",
1119 MOUNT_CIFS_VERSION_MAJOR,
1120 MOUNT_CIFS_VERSION_MINOR,
1121 MOUNT_CIFS_VENDOR_SUFFIX);
1122 exit (0);
1123 case 'w':
1124 flags &= ~MS_RDONLY;
1125 break;
1126 case 'R':
1127 rsize = atoi(optarg) ;
1128 break;
1129 case 'W':
1130 wsize = atoi(optarg);
1131 break;
1132 case '1':
1133 if (isdigit(*optarg)) {
1134 char *ep;
1136 uid = strtoul(optarg, &ep, 10);
1137 if (*ep) {
1138 printf("bad uid value \"%s\"\n", optarg);
1139 exit(1);
1141 } else {
1142 struct passwd *pw;
1144 if (!(pw = getpwnam(optarg))) {
1145 printf("bad user name \"%s\"\n", optarg);
1146 exit(1);
1148 uid = pw->pw_uid;
1149 endpwent();
1151 break;
1152 case '2':
1153 if (isdigit(*optarg)) {
1154 char *ep;
1156 gid = strtoul(optarg, &ep, 10);
1157 if (*ep) {
1158 printf("bad gid value \"%s\"\n", optarg);
1159 exit(1);
1161 } else {
1162 struct group *gr;
1164 if (!(gr = getgrnam(optarg))) {
1165 printf("bad user name \"%s\"\n", optarg);
1166 exit(1);
1168 gid = gr->gr_gid;
1169 endpwent();
1171 break;
1172 case 'u':
1173 got_user = 1;
1174 user_name = optarg;
1175 break;
1176 case 'd':
1177 domain_name = optarg; /* BB fix this - currently ignored */
1178 got_domain = 1;
1179 break;
1180 case 'p':
1181 if(mountpassword == NULL)
1182 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1183 if(mountpassword) {
1184 got_password = 1;
1185 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1187 break;
1188 case 'S':
1189 get_password_from_file(0 /* stdin */,NULL);
1190 break;
1191 case 't':
1192 break;
1193 default:
1194 printf("unknown mount option %c\n",c);
1195 mount_cifs_usage();
1196 exit(1);
1200 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1201 mount_cifs_usage();
1202 exit(1);
1205 if (getenv("PASSWD")) {
1206 if(mountpassword == NULL)
1207 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1208 if(mountpassword) {
1209 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1210 got_password = 1;
1212 } else if (getenv("PASSWD_FD")) {
1213 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1214 } else if (getenv("PASSWD_FILE")) {
1215 get_password_from_file(0, getenv("PASSWD_FILE"));
1218 if (orgoptions && parse_options(&orgoptions, &flags)) {
1219 rc = -1;
1220 goto mount_exit;
1222 ipaddr = parse_server(&share_name);
1223 if((ipaddr == NULL) && (got_ip == 0)) {
1224 printf("No ip address specified and hostname not found\n");
1225 rc = -1;
1226 goto mount_exit;
1229 /* BB save off path and pop after mount returns? */
1230 resolved_path = (char *)malloc(PATH_MAX+1);
1231 if(resolved_path) {
1232 /* Note that if we can not canonicalize the name, we get
1233 another chance to see if it is valid when we chdir to it */
1234 if (realpath(mountpoint, resolved_path)) {
1235 mountpoint = resolved_path;
1238 if(chdir(mountpoint)) {
1239 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1240 rc = -1;
1241 goto mount_exit;
1244 if(stat (".", &statbuf)) {
1245 printf("mount error: mount point %s does not exist\n",mountpoint);
1246 rc = -1;
1247 goto mount_exit;
1250 if (S_ISDIR(statbuf.st_mode) == 0) {
1251 printf("mount error: mount point %s is not a directory\n",mountpoint);
1252 rc = -1;
1253 goto mount_exit;
1256 if((getuid() != 0) && (geteuid() == 0)) {
1257 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1258 #ifndef CIFS_ALLOW_USR_SUID
1259 /* Do not allow user mounts to control suid flag
1260 for mount unless explicitly built that way */
1261 flags |= MS_NOSUID | MS_NODEV;
1262 #endif
1263 } else {
1264 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1265 return -1;
1269 if(got_user == 0) {
1270 user_name = getusername();
1271 got_user = 1;
1274 if(got_password == 0) {
1275 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1276 no good replacement yet. */
1277 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1278 if (!tmp_pass || !mountpassword) {
1279 printf("Password not entered, exiting\n");
1280 return -1;
1282 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1283 got_password = 1;
1285 /* FIXME launch daemon (handles dfs name resolution and credential change)
1286 remember to clear parms and overwrite password field before launching */
1287 mount_retry:
1288 if(orgoptions) {
1289 optlen = strlen(orgoptions);
1290 orgoptlen = optlen;
1291 } else
1292 optlen = 0;
1293 if(share_name)
1294 optlen += strlen(share_name) + 4;
1295 else {
1296 printf("No server share name specified\n");
1297 printf("\nMounting the DFS root for server not implemented yet\n");
1298 exit(1);
1300 if(user_name)
1301 optlen += strlen(user_name) + 6;
1302 if(ipaddr)
1303 optlen += strlen(ipaddr) + 4;
1304 if(mountpassword)
1305 optlen += strlen(mountpassword) + 6;
1306 SAFE_FREE(options);
1307 options_size = optlen + 10 + DOMAIN_SIZE;
1308 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 */);
1310 if(options == NULL) {
1311 printf("Could not allocate memory for mount options\n");
1312 return -1;
1315 options[0] = 0;
1316 strlcpy(options,"unc=",options_size);
1317 strlcat(options,share_name,options_size);
1318 /* scan backwards and reverse direction of slash */
1319 temp = strrchr(options, '/');
1320 if(temp > options + 6)
1321 *temp = '\\';
1322 if(ipaddr) {
1323 strlcat(options,",ip=",options_size);
1324 strlcat(options,ipaddr,options_size);
1327 if(user_name) {
1328 /* check for syntax like user=domain\user */
1329 if(got_domain == 0)
1330 domain_name = check_for_domain(&user_name);
1331 strlcat(options,",user=",options_size);
1332 strlcat(options,user_name,options_size);
1334 if(retry == 0) {
1335 if(domain_name) {
1336 /* extra length accounted for in option string above */
1337 strlcat(options,",domain=",options_size);
1338 strlcat(options,domain_name,options_size);
1341 if(mountpassword) {
1342 /* Commas have to be doubled, or else they will
1343 look like the parameter separator */
1344 /* if(sep is not set)*/
1345 if(retry == 0)
1346 check_for_comma(&mountpassword);
1347 strlcat(options,",pass=",options_size);
1348 strlcat(options,mountpassword,options_size);
1351 strlcat(options,",ver=",options_size);
1352 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1354 if(orgoptions) {
1355 strlcat(options,",",options_size);
1356 strlcat(options,orgoptions,options_size);
1358 if(prefixpath) {
1359 strlcat(options,",prefixpath=",options_size);
1360 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1362 if(verboseflag)
1363 printf("\nmount.cifs kernel mount options %s \n",options);
1365 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1366 replace_char(dev_name, '\\', '/', strlen(share_name));
1368 if(mount(dev_name, mountpoint, "cifs", flags, options)) {
1369 /* remember to kill daemon on error */
1370 switch (errno) {
1371 case 0:
1372 printf("mount failed but no error number set\n");
1373 break;
1374 case ENODEV:
1375 printf("mount error: cifs filesystem not supported by the system\n");
1376 break;
1377 case ENXIO:
1378 if(retry == 0) {
1379 retry = 1;
1380 if (uppercase_string(dev_name) &&
1381 uppercase_string(share_name) &&
1382 uppercase_string(prefixpath)) {
1383 printf("retrying with upper case share name\n");
1384 goto mount_retry;
1387 default:
1388 printf("mount error %d = %s\n",errno,strerror(errno));
1390 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1391 rc = -1;
1392 goto mount_exit;
1393 } else {
1394 pmntfile = setmntent(MOUNTED, "a+");
1395 if(pmntfile) {
1396 mountent.mnt_fsname = dev_name;
1397 mountent.mnt_dir = mountpoint;
1398 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1399 mountent.mnt_opts = (char *)malloc(220);
1400 if(mountent.mnt_opts) {
1401 char * mount_user = getusername();
1402 memset(mountent.mnt_opts,0,200);
1403 if(flags & MS_RDONLY)
1404 strlcat(mountent.mnt_opts,"ro",220);
1405 else
1406 strlcat(mountent.mnt_opts,"rw",220);
1407 if(flags & MS_MANDLOCK)
1408 strlcat(mountent.mnt_opts,",mand",220);
1409 if(flags & MS_NOEXEC)
1410 strlcat(mountent.mnt_opts,",noexec",220);
1411 if(flags & MS_NOSUID)
1412 strlcat(mountent.mnt_opts,",nosuid",220);
1413 if(flags & MS_NODEV)
1414 strlcat(mountent.mnt_opts,",nodev",220);
1415 if(flags & MS_SYNCHRONOUS)
1416 strlcat(mountent.mnt_opts,",synch",220);
1417 if(mount_user) {
1418 if(getuid() != 0) {
1419 strlcat(mountent.mnt_opts,",user=",220);
1420 strlcat(mountent.mnt_opts,mount_user,220);
1422 /* free(mount_user); do not free static mem */
1425 mountent.mnt_freq = 0;
1426 mountent.mnt_passno = 0;
1427 rc = addmntent(pmntfile,&mountent);
1428 endmntent(pmntfile);
1429 SAFE_FREE(mountent.mnt_opts);
1430 } else {
1431 printf("could not update mount table\n");
1434 rc = 0;
1435 mount_exit:
1436 if(mountpassword) {
1437 int len = strlen(mountpassword);
1438 memset(mountpassword,0,len);
1439 SAFE_FREE(mountpassword);
1442 SAFE_FREE(options);
1443 SAFE_FREE(orgoptions);
1444 SAFE_FREE(resolved_path);
1445 SAFE_FREE(share_name);
1446 return rc;