source: branches/samba-3.2.x/source/client/mount.cifs.c@ 198

Last change on this file since 198 was 141, checked in by Paul Smedley, 17 years ago

Update trunk to 3.2.1

File size: 39.0 KB
Line 
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)
5
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.
10
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.
15
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/>. */
18
19#ifndef _GNU_SOURCE
20#define _GNU_SOURCE
21#endif
22
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>
42
43#define MOUNT_CIFS_VERSION_MAJOR "1"
44#define MOUNT_CIFS_VERSION_MINOR "11"
45
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 */
58
59#ifndef MS_MOVE
60#define MS_MOVE 8192
61#endif
62
63#ifndef MS_BIND
64#define MS_BIND 4096
65#endif
66
67#define MAX_UNC_LEN 1024
68
69#define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
70
71#ifndef SAFE_FREE
72#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
73#endif
74
75#define MOUNT_PASSWD_SIZE 64
76#define DOMAIN_SIZE 64
77
78const char *thisprogram;
79int verboseflag = 0;
80static int got_password = 0;
81static int got_user = 0;
82static int got_domain = 0;
83static int got_ip = 0;
84static int got_unc = 0;
85static int got_uid = 0;
86static int got_gid = 0;
87static char * user_name = NULL;
88static char * mountpassword = NULL;
89char * domain_name = NULL;
90char * prefixpath = NULL;
91
92/* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
93 * don't link to libreplace so need them here. */
94
95/* like strncpy but does not 0 fill the buffer and always null
96 * terminates. bufsize is the size of the destination buffer */
97size_t strlcpy(char *d, const char *s, size_t bufsize)
98{
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;
106}
107
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 */
111size_t strlcat(char *d, const char *s, size_t bufsize)
112{
113 size_t len1 = strlen(d);
114 size_t len2 = strlen(s);
115 size_t ret = len1 + len2;
116
117 if (len1+len2 >= bufsize) {
118 if (bufsize < (len1+1)) {
119 return ret;
120 }
121 len2 = bufsize - (len1+1);
122 }
123 if (len2 > 0) {
124 memcpy(d+len1, s, len2);
125 d[len1+len2] = 0;
126 }
127 return ret;
128}
129
130/* BB finish BB
131
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.
136
137BB end finish BB */
138
139static char * check_for_domain(char **);
140
141
142static void mount_cifs_usage(void)
143{
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);
165
166 SAFE_FREE(mountpassword);
167 exit(1);
168}
169
170/* caller frees username if necessary */
171static char * getusername(void) {
172 char *username = NULL;
173 struct passwd *password = getpwuid(getuid());
174
175 if (password) {
176 username = password->pw_name;
177 }
178 return username;
179}
180
181static char * parse_cifs_url(char * unc_name)
182{
183 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
184 return NULL;
185}
186
187static int open_cred_file(char * file_name)
188{
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;
200 }
201
202 while(fgets(line_buf,4096,fs)) {
203 /* parse line from credential file */
204
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 */
210 }
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;
221 }
222 }
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);
233 }
234 }
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;
245 }
246 }
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;
259 }
260 }
261 }
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;
274 }
275 }
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;
287 }
288 }
289 }
290 }
291
292 }
293 fclose(fs);
294 SAFE_FREE(line_buf);
295 return 0;
296}
297
298static int get_password_from_file(int file_descript, char * filename)
299{
300 int rc = 0;
301 int i;
302 char c;
303
304 if(mountpassword == NULL)
305 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
306 else
307 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
308
309 if (mountpassword == NULL) {
310 printf("malloc failed\n");
311 exit(1);
312 }
313
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);
320 }
321 }
322 /* else file already open and fd provided */
323
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");
335 }
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;
343 }
344 }
345 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
346 printf("\nWarning: password longer than %d characters specified in cifs password file",
347 MOUNT_PASSWD_SIZE);
348 }
349 got_password = 1;
350 if(filename != NULL) {
351 close(file_descript);
352 }
353
354 return rc;
355}
356
357static int parse_options(char ** optionsp, int * filesys_flags)
358{
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];
369
370 if (!optionsp || !*optionsp)
371 return 1;
372 data = *optionsp;
373
374 if(verboseflag)
375 printf("parsing options: %s\n", data);
376
377 /* BB fixme check for separator override BB */
378
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());
384 }
385
386/* while ((data = strsep(&options, ",")) != NULL) { */
387 while(data != NULL) {
388 /* check if ends with trailing comma */
389 if(*data == 0)
390 break;
391
392 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
393 /* data = next keyword */
394 /* value = next value ie stuff after equal sign */
395
396 next_keyword = strchr(data,','); /* BB handle sep= */
397
398 /* temporarily null terminate end of keyword=value pair */
399 if(next_keyword)
400 *next_keyword++ = 0;
401
402 /* temporarily null terminate keyword to make keyword and value distinct */
403 if ((value = strchr(data, '=')) != NULL) {
404 *value = '\0';
405 value++;
406 }
407
408 if (strncmp(data, "users",5) == 0) {
409 if(!value || !*value) {
410 goto nocopy;
411 }
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) {
415
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; */
426 }
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++;
445 }
446 }
447 }
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;
458 }
459 }
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;
473 }
474 } else if (strncmp(data, "sec", 3) == 0) {
475 if (value) {
476 if (!strcmp(value, "none"))
477 got_password = 1;
478 }
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;
489 }
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");
498 }
499
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;
515 }
516 } else {
517 printf("CIFS: UNC name too long\n");
518 return 1;
519 }
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; */
525 }
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;
531 }
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 opening credential file %s\n",rc, value);
537 return 1;
538 }
539 } else {
540 printf("invalid credential file name specified\n");
541 return 1;
542 }
543 } else if (strncmp(data, "uid", 3) == 0) {
544 if (value && *value) {
545 got_uid = 1;
546 if (!isdigit(*value)) {
547 struct passwd *pw;
548
549 if (!(pw = getpwnam(value))) {
550 printf("bad user name \"%s\"\n", value);
551 exit(1);
552 }
553 snprintf(user, sizeof(user), "%u", pw->pw_uid);
554 } else {
555 strlcpy(user,value,sizeof(user));
556 }
557 }
558 goto nocopy;
559 } else if (strncmp(data, "gid", 3) == 0) {
560 if (value && *value) {
561 got_gid = 1;
562 if (!isdigit(*value)) {
563 struct group *gr;
564
565 if (!(gr = getgrnam(value))) {
566 printf("bad group name \"%s\"\n", value);
567 exit(1);
568 }
569 snprintf(group, sizeof(group), "%u", gr->gr_gid);
570 } else {
571 strlcpy(group,value,sizeof(group));
572 }
573 }
574 goto nocopy;
575 /* fmask and dmask synonyms for people used to smbfs syntax */
576 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
577 if (!value || !*value) {
578 printf ("Option '%s' requires a numerical argument\n", data);
579 return 1;
580 }
581
582 if (value[0] != '0') {
583 printf ("WARNING: '%s' not expressed in octal.\n", data);
584 }
585
586 if (strcmp (data, "fmask") == 0) {
587 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
588 data = "file_mode"; /* BB fix this */
589 }
590 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
591 if (!value || !*value) {
592 printf ("Option '%s' requires a numerical argument\n", data);
593 return 1;
594 }
595
596 if (value[0] != '0') {
597 printf ("WARNING: '%s' not expressed in octal.\n", data);
598 }
599
600 if (strcmp (data, "dmask") == 0) {
601 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
602 data = "dir_mode";
603 }
604 /* the following eight mount options should be
605 stripped out from what is passed into the kernel
606 since these eight options are best passed as the
607 mount flags rather than redundantly to the kernel
608 and could generate spurious warnings depending on the
609 level of the corresponding cifs vfs kernel code */
610 } else if (strncmp(data, "nosuid", 6) == 0) {
611 *filesys_flags |= MS_NOSUID;
612 } else if (strncmp(data, "suid", 4) == 0) {
613 *filesys_flags &= ~MS_NOSUID;
614 } else if (strncmp(data, "nodev", 5) == 0) {
615 *filesys_flags |= MS_NODEV;
616 } else if ((strncmp(data, "nobrl", 5) == 0) ||
617 (strncmp(data, "nolock", 6) == 0)) {
618 *filesys_flags &= ~MS_MANDLOCK;
619 } else if (strncmp(data, "dev", 3) == 0) {
620 *filesys_flags &= ~MS_NODEV;
621 } else if (strncmp(data, "noexec", 6) == 0) {
622 *filesys_flags |= MS_NOEXEC;
623 } else if (strncmp(data, "exec", 4) == 0) {
624 *filesys_flags &= ~MS_NOEXEC;
625 } else if (strncmp(data, "guest", 5) == 0) {
626 got_password=1;
627 } else if (strncmp(data, "ro", 2) == 0) {
628 *filesys_flags |= MS_RDONLY;
629 } else if (strncmp(data, "rw", 2) == 0) {
630 *filesys_flags &= ~MS_RDONLY;
631 } else if (strncmp(data, "remount", 7) == 0) {
632 *filesys_flags |= MS_REMOUNT;
633 } /* else if (strnicmp(data, "port", 4) == 0) {
634 if (value && *value) {
635 vol->port =
636 simple_strtoul(value, &value, 0);
637 }
638 } else if (strnicmp(data, "rsize", 5) == 0) {
639 if (value && *value) {
640 vol->rsize =
641 simple_strtoul(value, &value, 0);
642 }
643 } else if (strnicmp(data, "wsize", 5) == 0) {
644 if (value && *value) {
645 vol->wsize =
646 simple_strtoul(value, &value, 0);
647 }
648 } else if (strnicmp(data, "version", 3) == 0) {
649 } else {
650 printf("CIFS: Unknown mount option %s\n",data);
651 } */ /* nothing to do on those four mount options above.
652 Just pass to kernel and ignore them here */
653
654 /* Copy (possibly modified) option to out */
655 word_len = strlen(data);
656 if (value)
657 word_len += 1 + strlen(value);
658
659 out = (char *)realloc(out, out_len + word_len + 2);
660 if (out == NULL) {
661 perror("malloc");
662 exit(1);
663 }
664
665 if (out_len) {
666 strlcat(out, ",", out_len + word_len + 2);
667 out_len++;
668 }
669
670 if (value)
671 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
672 else
673 snprintf(out + out_len, word_len + 1, "%s", data);
674 out_len = strlen(out);
675
676nocopy:
677 data = next_keyword;
678 }
679
680 /* special-case the uid and gid */
681 if (got_uid) {
682 word_len = strlen(user);
683
684 out = (char *)realloc(out, out_len + word_len + 6);
685 if (out == NULL) {
686 perror("malloc");
687 exit(1);
688 }
689
690 if (out_len) {
691 strlcat(out, ",", out_len + word_len + 6);
692 out_len++;
693 }
694 snprintf(out + out_len, word_len + 5, "uid=%s", user);
695 out_len = strlen(out);
696 }
697 if (got_gid) {
698 word_len = strlen(group);
699
700 out = (char *)realloc(out, out_len + 1 + word_len + 6);
701 if (out == NULL) {
702 perror("malloc");
703 exit(1);
704 }
705
706 if (out_len) {
707 strlcat(out, ",", out_len + word_len + 6);
708 out_len++;
709 }
710 snprintf(out + out_len, word_len + 5, "gid=%s", group);
711 out_len = strlen(out);
712 }
713
714 SAFE_FREE(*optionsp);
715 *optionsp = out;
716 return 0;
717}
718
719/* replace all (one or more) commas with double commas */
720static void check_for_comma(char ** ppasswrd)
721{
722 char *new_pass_buf;
723 char *pass;
724 int i,j;
725 int number_of_commas = 0;
726 int len;
727
728 if(ppasswrd == NULL)
729 return;
730 else
731 (pass = *ppasswrd);
732
733 len = strlen(pass);
734
735 for(i=0;i<len;i++) {
736 if(pass[i] == ',')
737 number_of_commas++;
738 }
739
740 if(number_of_commas == 0)
741 return;
742 if(number_of_commas > MOUNT_PASSWD_SIZE) {
743 /* would otherwise overflow the mount options buffer */
744 printf("\nInvalid password. Password contains too many commas.\n");
745 return;
746 }
747
748 new_pass_buf = (char *)malloc(len+number_of_commas+1);
749 if(new_pass_buf == NULL)
750 return;
751
752 for(i=0,j=0;i<len;i++,j++) {
753 new_pass_buf[j] = pass[i];
754 if(pass[i] == ',') {
755 j++;
756 new_pass_buf[j] = pass[i];
757 }
758 }
759 new_pass_buf[len+number_of_commas] = 0;
760
761 SAFE_FREE(*ppasswrd);
762 *ppasswrd = new_pass_buf;
763
764 return;
765}
766
767/* Usernames can not have backslash in them and we use
768 [BB check if usernames can have forward slash in them BB]
769 backslash as domain\user separator character
770*/
771static char * check_for_domain(char **ppuser)
772{
773 char * original_string;
774 char * usernm;
775 char * domainnm;
776 int original_len;
777 int len;
778 int i;
779
780 if(ppuser == NULL)
781 return NULL;
782
783 original_string = *ppuser;
784
785 if (original_string == NULL)
786 return NULL;
787
788 original_len = strlen(original_string);
789
790 usernm = strchr(*ppuser,'/');
791 if (usernm == NULL) {
792 usernm = strchr(*ppuser,'\\');
793 if (usernm == NULL)
794 return NULL;
795 }
796
797 if(got_domain) {
798 printf("Domain name specified twice. Username probably malformed\n");
799 return NULL;
800 }
801
802 usernm[0] = 0;
803 domainnm = *ppuser;
804 if (domainnm[0] != 0) {
805 got_domain = 1;
806 } else {
807 printf("null domain\n");
808 }
809 len = strlen(domainnm);
810 /* reset domainm to new buffer, and copy
811 domain name into it */
812 domainnm = (char *)malloc(len+1);
813 if(domainnm == NULL)
814 return NULL;
815
816 strlcpy(domainnm,*ppuser,len+1);
817
818/* move_string(*ppuser, usernm+1) */
819 len = strlen(usernm+1);
820
821 if(len >= original_len) {
822 /* should not happen */
823 return domainnm;
824 }
825
826 for(i=0;i<original_len;i++) {
827 if(i<len)
828 original_string[i] = usernm[i+1];
829 else /* stuff with commas to remove last parm */
830 original_string[i] = ',';
831 }
832
833 /* BB add check for more than one slash?
834 strchr(*ppuser,'/');
835 strchr(*ppuser,'\\')
836 */
837
838 return domainnm;
839}
840
841/* replace all occurances of "from" in a string with "to" */
842static void replace_char(char *string, char from, char to, int maxlen)
843{
844 char *lastchar = string + maxlen;
845 while (string) {
846 string = strchr(string, from);
847 if (string) {
848 *string = to;
849 if (string >= lastchar)
850 return;
851 }
852 }
853}
854
855/* Note that caller frees the returned buffer if necessary */
856static char * parse_server(char ** punc_name)
857{
858 char * unc_name = *punc_name;
859 int length = strnlen(unc_name, MAX_UNC_LEN);
860 char * share;
861 char * ipaddress_string = NULL;
862 struct hostent * host_entry = NULL;
863 struct in_addr server_ipaddr;
864
865 if(length > (MAX_UNC_LEN - 1)) {
866 printf("mount error: UNC name too long");
867 return NULL;
868 }
869 if (strncasecmp("cifs://",unc_name,7) == 0)
870 return parse_cifs_url(unc_name+7);
871 if (strncasecmp("smb://",unc_name,6) == 0) {
872 return parse_cifs_url(unc_name+6);
873 }
874
875 if(length < 3) {
876 /* BB add code to find DFS root here */
877 printf("\nMounting the DFS root for domain not implemented yet\n");
878 return NULL;
879 } else {
880 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
881 /* check for nfs syntax ie server:share */
882 share = strchr(unc_name,':');
883 if(share) {
884 *punc_name = (char *)malloc(length+3);
885 if(*punc_name == NULL) {
886 /* put the original string back if
887 no memory left */
888 *punc_name = unc_name;
889 return NULL;
890 }
891 *share = '/';
892 strlcpy((*punc_name)+2,unc_name,length+1);
893 SAFE_FREE(unc_name);
894 unc_name = *punc_name;
895 unc_name[length+2] = 0;
896 goto continue_unc_parsing;
897 } else {
898 printf("mount error: improperly formatted UNC name.");
899 printf(" %s does not begin with \\\\ or //\n",unc_name);
900 return NULL;
901 }
902 } else {
903continue_unc_parsing:
904 unc_name[0] = '/';
905 unc_name[1] = '/';
906 unc_name += 2;
907
908 /* allow for either delimiter between host and sharename */
909 if ((share = strpbrk(unc_name, "/\\"))) {
910 *share = 0; /* temporarily terminate the string */
911 share += 1;
912 if(got_ip == 0) {
913 host_entry = gethostbyname(unc_name);
914 }
915 *(share - 1) = '/'; /* put delimiter back */
916
917 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
918 if ((prefixpath = strpbrk(share, "/\\"))) {
919 *prefixpath = 0; /* permanently terminate the string */
920 if (!strlen(++prefixpath))
921 prefixpath = NULL; /* this needs to be done explicitly */
922 }
923 if(got_ip) {
924 if(verboseflag)
925 printf("ip address specified explicitly\n");
926 return NULL;
927 }
928 if(host_entry == NULL) {
929 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
930 return NULL;
931 } else {
932 /* BB should we pass an alternate version of the share name as Unicode */
933 /* BB what about ipv6? BB */
934 /* BB add retries with alternate servers in list */
935
936 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
937
938 ipaddress_string = inet_ntoa(server_ipaddr);
939 if(ipaddress_string == NULL) {
940 printf("mount error: could not get valid ip address for target server\n");
941 return NULL;
942 }
943 return ipaddress_string;
944 }
945 } else {
946 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
947 printf("Mounting the DFS root for a particular server not implemented yet\n");
948 return NULL;
949 }
950 }
951 }
952}
953
954static struct option longopts[] = {
955 { "all", 0, NULL, 'a' },
956 { "help",0, NULL, 'h' },
957 { "move",0, NULL, 'm' },
958 { "bind",0, NULL, 'b' },
959 { "read-only", 0, NULL, 'r' },
960 { "ro", 0, NULL, 'r' },
961 { "verbose", 0, NULL, 'v' },
962 { "version", 0, NULL, 'V' },
963 { "read-write", 0, NULL, 'w' },
964 { "rw", 0, NULL, 'w' },
965 { "options", 1, NULL, 'o' },
966 { "type", 1, NULL, 't' },
967 { "rsize",1, NULL, 'R' },
968 { "wsize",1, NULL, 'W' },
969 { "uid", 1, NULL, '1'},
970 { "gid", 1, NULL, '2'},
971 { "user",1,NULL,'u'},
972 { "username",1,NULL,'u'},
973 { "dom",1,NULL,'d'},
974 { "domain",1,NULL,'d'},
975 { "password",1,NULL,'p'},
976 { "pass",1,NULL,'p'},
977 { "credentials",1,NULL,'c'},
978 { "port",1,NULL,'P'},
979 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
980 { NULL, 0, NULL, 0 }
981};
982
983/* convert a string to uppercase. return false if the string
984 * wasn't ASCII or was a NULL ptr */
985static int
986uppercase_string(char *string)
987{
988 if (!string)
989 return 0;
990
991 while (*string) {
992 /* check for unicode */
993 if ((unsigned char) string[0] & 0x80)
994 return 0;
995 *string = toupper((unsigned char) *string);
996 string++;
997 }
998
999 return 1;
1000}
1001
1002int main(int argc, char ** argv)
1003{
1004 int c;
1005 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1006 char * orgoptions = NULL;
1007 char * share_name = NULL;
1008 char * ipaddr = NULL;
1009 char * uuid = NULL;
1010 char * mountpoint = NULL;
1011 char * options = NULL;
1012 char * resolved_path = NULL;
1013 char * temp;
1014 char * dev_name;
1015 int rc;
1016 int rsize = 0;
1017 int wsize = 0;
1018 int nomtab = 0;
1019 int uid = 0;
1020 int gid = 0;
1021 int optlen = 0;
1022 int orgoptlen = 0;
1023 size_t options_size = 0;
1024 int retry = 0; /* set when we have to retry mount with uppercase */
1025 struct stat statbuf;
1026 struct utsname sysinfo;
1027 struct mntent mountent;
1028 FILE * pmntfile;
1029
1030 /* setlocale(LC_ALL, "");
1031 bindtextdomain(PACKAGE, LOCALEDIR);
1032 textdomain(PACKAGE); */
1033
1034 if(argc && argv) {
1035 thisprogram = argv[0];
1036 } else {
1037 mount_cifs_usage();
1038 exit(1);
1039 }
1040
1041 if(thisprogram == NULL)
1042 thisprogram = "mount.cifs";
1043
1044 uname(&sysinfo);
1045 /* BB add workstation name and domain and pass down */
1046
1047/* #ifdef _GNU_SOURCE
1048 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1049#endif */
1050 if(argc > 2) {
1051 dev_name = argv[1];
1052 share_name = strndup(argv[1], MAX_UNC_LEN);
1053 if (share_name == NULL) {
1054 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1055 exit(1);
1056 }
1057 mountpoint = argv[2];
1058 } else {
1059 mount_cifs_usage();
1060 exit(1);
1061 }
1062
1063 /* add sharename in opts string as unc= parm */
1064
1065 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1066 longopts, NULL)) != -1) {
1067 switch (c) {
1068/* No code to do the following options yet */
1069/* case 'l':
1070 list_with_volumelabel = 1;
1071 break;
1072 case 'L':
1073 volumelabel = optarg;
1074 break; */
1075/* case 'a':
1076 ++mount_all;
1077 break; */
1078
1079 case '?':
1080 case 'h': /* help */
1081 mount_cifs_usage ();
1082 exit(1);
1083 case 'n':
1084 ++nomtab;
1085 break;
1086 case 'b':
1087#ifdef MS_BIND
1088 flags |= MS_BIND;
1089#else
1090 fprintf(stderr,
1091 "option 'b' (MS_BIND) not supported\n");
1092#endif
1093 break;
1094 case 'm':
1095#ifdef MS_MOVE
1096 flags |= MS_MOVE;
1097#else
1098 fprintf(stderr,
1099 "option 'm' (MS_MOVE) not supported\n");
1100#endif
1101 break;
1102 case 'o':
1103 orgoptions = strdup(optarg);
1104 break;
1105 case 'r': /* mount readonly */
1106 flags |= MS_RDONLY;
1107 break;
1108 case 'U':
1109 uuid = optarg;
1110 break;
1111 case 'v':
1112 ++verboseflag;
1113 break;
1114 case 'V':
1115 printf ("mount.cifs version: %s.%s%s\n",
1116 MOUNT_CIFS_VERSION_MAJOR,
1117 MOUNT_CIFS_VERSION_MINOR,
1118 MOUNT_CIFS_VENDOR_SUFFIX);
1119 exit (0);
1120 case 'w':
1121 flags &= ~MS_RDONLY;
1122 break;
1123 case 'R':
1124 rsize = atoi(optarg) ;
1125 break;
1126 case 'W':
1127 wsize = atoi(optarg);
1128 break;
1129 case '1':
1130 if (isdigit(*optarg)) {
1131 char *ep;
1132
1133 uid = strtoul(optarg, &ep, 10);
1134 if (*ep) {
1135 printf("bad uid value \"%s\"\n", optarg);
1136 exit(1);
1137 }
1138 } else {
1139 struct passwd *pw;
1140
1141 if (!(pw = getpwnam(optarg))) {
1142 printf("bad user name \"%s\"\n", optarg);
1143 exit(1);
1144 }
1145 uid = pw->pw_uid;
1146 endpwent();
1147 }
1148 break;
1149 case '2':
1150 if (isdigit(*optarg)) {
1151 char *ep;
1152
1153 gid = strtoul(optarg, &ep, 10);
1154 if (*ep) {
1155 printf("bad gid value \"%s\"\n", optarg);
1156 exit(1);
1157 }
1158 } else {
1159 struct group *gr;
1160
1161 if (!(gr = getgrnam(optarg))) {
1162 printf("bad user name \"%s\"\n", optarg);
1163 exit(1);
1164 }
1165 gid = gr->gr_gid;
1166 endpwent();
1167 }
1168 break;
1169 case 'u':
1170 got_user = 1;
1171 user_name = optarg;
1172 break;
1173 case 'd':
1174 domain_name = optarg; /* BB fix this - currently ignored */
1175 got_domain = 1;
1176 break;
1177 case 'p':
1178 if(mountpassword == NULL)
1179 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1180 if(mountpassword) {
1181 got_password = 1;
1182 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1183 }
1184 break;
1185 case 'S':
1186 get_password_from_file(0 /* stdin */,NULL);
1187 break;
1188 case 't':
1189 break;
1190 default:
1191 printf("unknown mount option %c\n",c);
1192 mount_cifs_usage();
1193 exit(1);
1194 }
1195 }
1196
1197 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1198 mount_cifs_usage();
1199 exit(1);
1200 }
1201
1202 if (getenv("PASSWD")) {
1203 if(mountpassword == NULL)
1204 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1205 if(mountpassword) {
1206 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1207 got_password = 1;
1208 }
1209 } else if (getenv("PASSWD_FD")) {
1210 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1211 } else if (getenv("PASSWD_FILE")) {
1212 get_password_from_file(0, getenv("PASSWD_FILE"));
1213 }
1214
1215 if (orgoptions && parse_options(&orgoptions, &flags)) {
1216 rc = -1;
1217 goto mount_exit;
1218 }
1219 ipaddr = parse_server(&share_name);
1220 if((ipaddr == NULL) && (got_ip == 0)) {
1221 printf("No ip address specified and hostname not found\n");
1222 rc = -1;
1223 goto mount_exit;
1224 }
1225
1226 /* BB save off path and pop after mount returns? */
1227 resolved_path = (char *)malloc(PATH_MAX+1);
1228 if(resolved_path) {
1229 /* Note that if we can not canonicalize the name, we get
1230 another chance to see if it is valid when we chdir to it */
1231 if (realpath(mountpoint, resolved_path)) {
1232 mountpoint = resolved_path;
1233 }
1234 }
1235 if(chdir(mountpoint)) {
1236 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1237 rc = -1;
1238 goto mount_exit;
1239 }
1240
1241 if(stat (".", &statbuf)) {
1242 printf("mount error: mount point %s does not exist\n",mountpoint);
1243 rc = -1;
1244 goto mount_exit;
1245 }
1246
1247 if (S_ISDIR(statbuf.st_mode) == 0) {
1248 printf("mount error: mount point %s is not a directory\n",mountpoint);
1249 rc = -1;
1250 goto mount_exit;
1251 }
1252
1253 if((getuid() != 0) && (geteuid() == 0)) {
1254 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1255#ifndef CIFS_ALLOW_USR_SUID
1256 /* Do not allow user mounts to control suid flag
1257 for mount unless explicitly built that way */
1258 flags |= MS_NOSUID | MS_NODEV;
1259#endif
1260 } else {
1261 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1262 return -1;
1263 }
1264 }
1265
1266 if(got_user == 0) {
1267 user_name = getusername();
1268 got_user = 1;
1269 }
1270
1271 if(got_password == 0) {
1272 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1273 no good replacement yet. */
1274 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1275 if (!tmp_pass || !mountpassword) {
1276 printf("Password not entered, exiting\n");
1277 return -1;
1278 }
1279 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1280 got_password = 1;
1281 }
1282 /* FIXME launch daemon (handles dfs name resolution and credential change)
1283 remember to clear parms and overwrite password field before launching */
1284mount_retry:
1285 if(orgoptions) {
1286 optlen = strlen(orgoptions);
1287 orgoptlen = optlen;
1288 } else
1289 optlen = 0;
1290 if(share_name)
1291 optlen += strlen(share_name) + 4;
1292 else {
1293 printf("No server share name specified\n");
1294 printf("\nMounting the DFS root for server not implemented yet\n");
1295 exit(1);
1296 }
1297 if(user_name)
1298 optlen += strlen(user_name) + 6;
1299 if(ipaddr)
1300 optlen += strlen(ipaddr) + 4;
1301 if(mountpassword)
1302 optlen += strlen(mountpassword) + 6;
1303 SAFE_FREE(options);
1304 options_size = optlen + 10 + DOMAIN_SIZE;
1305 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 */);
1306
1307 if(options == NULL) {
1308 printf("Could not allocate memory for mount options\n");
1309 return -1;
1310 }
1311
1312 options[0] = 0;
1313 strlcpy(options,"unc=",options_size);
1314 strlcat(options,share_name,options_size);
1315 /* scan backwards and reverse direction of slash */
1316 temp = strrchr(options, '/');
1317 if(temp > options + 6)
1318 *temp = '\\';
1319 if(ipaddr) {
1320 strlcat(options,",ip=",options_size);
1321 strlcat(options,ipaddr,options_size);
1322 }
1323
1324 if(user_name) {
1325 /* check for syntax like user=domain\user */
1326 if(got_domain == 0)
1327 domain_name = check_for_domain(&user_name);
1328 strlcat(options,",user=",options_size);
1329 strlcat(options,user_name,options_size);
1330 }
1331 if(retry == 0) {
1332 if(domain_name) {
1333 /* extra length accounted for in option string above */
1334 strlcat(options,",domain=",options_size);
1335 strlcat(options,domain_name,options_size);
1336 }
1337 }
1338 if(mountpassword) {
1339 /* Commas have to be doubled, or else they will
1340 look like the parameter separator */
1341/* if(sep is not set)*/
1342 if(retry == 0)
1343 check_for_comma(&mountpassword);
1344 strlcat(options,",pass=",options_size);
1345 strlcat(options,mountpassword,options_size);
1346 }
1347
1348 strlcat(options,",ver=",options_size);
1349 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1350
1351 if(orgoptions) {
1352 strlcat(options,",",options_size);
1353 strlcat(options,orgoptions,options_size);
1354 }
1355 if(prefixpath) {
1356 strlcat(options,",prefixpath=",options_size);
1357 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1358 }
1359 if(verboseflag)
1360 printf("\nmount.cifs kernel mount options %s \n",options);
1361
1362 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1363 replace_char(dev_name, '\\', '/', strlen(share_name));
1364
1365 if(mount(dev_name, mountpoint, "cifs", flags, options)) {
1366 /* remember to kill daemon on error */
1367 switch (errno) {
1368 case 0:
1369 printf("mount failed but no error number set\n");
1370 break;
1371 case ENODEV:
1372 printf("mount error: cifs filesystem not supported by the system\n");
1373 break;
1374 case ENXIO:
1375 if(retry == 0) {
1376 retry = 1;
1377 if (uppercase_string(dev_name) &&
1378 uppercase_string(share_name) &&
1379 uppercase_string(prefixpath)) {
1380 printf("retrying with upper case share name\n");
1381 goto mount_retry;
1382 }
1383 }
1384 default:
1385 printf("mount error %d = %s\n",errno,strerror(errno));
1386 }
1387 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1388 rc = -1;
1389 goto mount_exit;
1390 } else {
1391 pmntfile = setmntent(MOUNTED, "a+");
1392 if(pmntfile) {
1393 mountent.mnt_fsname = dev_name;
1394 mountent.mnt_dir = mountpoint;
1395 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1396 mountent.mnt_opts = (char *)malloc(220);
1397 if(mountent.mnt_opts) {
1398 char * mount_user = getusername();
1399 memset(mountent.mnt_opts,0,200);
1400 if(flags & MS_RDONLY)
1401 strlcat(mountent.mnt_opts,"ro",220);
1402 else
1403 strlcat(mountent.mnt_opts,"rw",220);
1404 if(flags & MS_MANDLOCK)
1405 strlcat(mountent.mnt_opts,",mand",220);
1406 if(flags & MS_NOEXEC)
1407 strlcat(mountent.mnt_opts,",noexec",220);
1408 if(flags & MS_NOSUID)
1409 strlcat(mountent.mnt_opts,",nosuid",220);
1410 if(flags & MS_NODEV)
1411 strlcat(mountent.mnt_opts,",nodev",220);
1412 if(flags & MS_SYNCHRONOUS)
1413 strlcat(mountent.mnt_opts,",synch",220);
1414 if(mount_user) {
1415 if(getuid() != 0) {
1416 strlcat(mountent.mnt_opts,",user=",220);
1417 strlcat(mountent.mnt_opts,mount_user,220);
1418 }
1419 /* free(mount_user); do not free static mem */
1420 }
1421 }
1422 mountent.mnt_freq = 0;
1423 mountent.mnt_passno = 0;
1424 rc = addmntent(pmntfile,&mountent);
1425 endmntent(pmntfile);
1426 SAFE_FREE(mountent.mnt_opts);
1427 } else {
1428 printf("could not update mount table\n");
1429 }
1430 }
1431 rc = 0;
1432mount_exit:
1433 if(mountpassword) {
1434 int len = strlen(mountpassword);
1435 memset(mountpassword,0,len);
1436 SAFE_FREE(mountpassword);
1437 }
1438
1439 SAFE_FREE(options);
1440 SAFE_FREE(orgoptions);
1441 SAFE_FREE(resolved_path);
1442 SAFE_FREE(share_name);
1443 return rc;
1444}
Note: See TracBrowser for help on using the repository browser.