1 /*
2 * Copyright (C) 2016 Jakub Kruszona-Zawadzki, Core Technology Sp. z o.o.
3 *
4 * This file is part of MooseFS.
5 *
6 * MooseFS 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, version 2 (only).
9 *
10 * MooseFS is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with MooseFS; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301, USA
18 * or visit http://www.gnu.org/licenses/gpl-2.0.html
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <syslog.h>
31 #include <errno.h>
32 #include <pwd.h>
33 #include <grp.h>
34 #include <inttypes.h>
35
36 #include "MFSCommunication.h"
37 #include "md5.h"
38 #include "exports.h"
39 #include "datapack.h"
40 #include "main.h"
41 #include "cfg.h"
42 #include "slogger.h"
43 #include "massert.h"
44
45 typedef struct _exports {
46 uint32_t pleng;
47 const uint8_t *path; // without '/' at the begin and at the end
48 uint32_t fromip,toip;
49 uint32_t minversion;
50 uint8_t passworddigest[16];
51 unsigned alldirs:1;
52 unsigned needpassword:1;
53 unsigned meta:1;
54 unsigned rootredefined:1;
55 // unsigned old:1;
56 uint8_t sesflags;
57 uint8_t mingoal;
58 uint8_t maxgoal;
59 uint32_t mintrashtime;
60 uint32_t maxtrashtime;
61 uint32_t rootuid;
62 uint32_t rootgid;
63 uint32_t mapalluid;
64 uint32_t mapallgid;
65 struct _exports *next;
66 } exports;
67
68 static exports *exports_records;
69 static char *ExportsFileName;
70
exports_strsep(char ** stringp,const char * delim)71 char* exports_strsep(char **stringp, const char *delim) {
72 char *s;
73 const char *spanp;
74 int c, sc;
75 char *tok;
76
77 s = *stringp;
78 if (s==NULL) {
79 return NULL;
80 }
81 while (*s==' ' || *s=='\t') {
82 s++;
83 }
84 tok = s;
85 while (1) {
86 c = *s++;
87 spanp = delim;
88 do {
89 if ((sc=*spanp++)==c) {
90 if (c==0) {
91 *stringp = NULL;
92 } else {
93 *stringp = s;
94 }
95 s--;
96 while ((s>tok) && (s[-1]==' ' || s[-1]=='\t')) {
97 s--;
98 }
99 *s = 0;
100 return tok;
101 }
102 } while (sc!=0);
103 }
104 return NULL; // unreachable
105 }
106
107
exports_info_size(uint8_t versmode)108 uint32_t exports_info_size(uint8_t versmode) {
109 exports *e;
110 uint32_t size=0;
111 for (e=exports_records ; e ; e=e->next) {
112 if (e->meta) {
113 size+=35+((versmode)?10:0);
114 } else {
115 size+=35+((versmode)?10:0)+e->pleng;
116 }
117 }
118 return size;
119 }
120
exports_info_data(uint8_t versmode,uint8_t * buff)121 void exports_info_data(uint8_t versmode,uint8_t *buff) {
122 exports *e;
123 for (e=exports_records ; e ; e=e->next) {
124 put32bit(&buff,e->fromip);
125 put32bit(&buff,e->toip);
126 if (e->meta) {
127 put32bit(&buff,1);
128 put8bit(&buff,'.');
129 } else {
130 put32bit(&buff,e->pleng+1);
131 put8bit(&buff,'/');
132 if (e->pleng>0) {
133 memcpy(buff,e->path,e->pleng);
134 buff+=e->pleng;
135 }
136 }
137 put32bit(&buff,e->minversion);
138 put8bit(&buff,(e->alldirs?1:0)+(e->needpassword?2:0));
139 put8bit(&buff,e->sesflags);
140 put32bit(&buff,e->rootuid);
141 put32bit(&buff,e->rootgid);
142 put32bit(&buff,e->mapalluid);
143 put32bit(&buff,e->mapallgid);
144 if (versmode) {
145 put8bit(&buff,e->mingoal);
146 put8bit(&buff,e->maxgoal);
147 put32bit(&buff,e->mintrashtime);
148 put32bit(&buff,e->maxtrashtime);
149 }
150 }
151 }
152
exports_check(uint32_t ip,uint32_t version,uint8_t meta,const uint8_t * path,const uint8_t rndcode[32],const uint8_t passcode[16],uint8_t * sesflags,uint32_t * rootuid,uint32_t * rootgid,uint32_t * mapalluid,uint32_t * mapallgid,uint8_t * mingoal,uint8_t * maxgoal,uint32_t * mintrashtime,uint32_t * maxtrashtime)153 uint8_t exports_check(uint32_t ip,uint32_t version,uint8_t meta,const uint8_t *path,const uint8_t rndcode[32],const uint8_t passcode[16],uint8_t *sesflags,uint32_t *rootuid,uint32_t *rootgid,uint32_t *mapalluid,uint32_t *mapallgid,uint8_t *mingoal,uint8_t *maxgoal,uint32_t *mintrashtime,uint32_t *maxtrashtime) {
154 const uint8_t *p;
155 uint32_t pleng,i;
156 uint8_t rndstate;
157 int ok,nopass;
158 md5ctx md5c;
159 uint8_t entrydigest[16];
160 exports *e,*f;
161
162 // syslog(LOG_NOTICE,"check exports for: %u.%u.%u.%u:%s",(ip>>24)&0xFF,(ip>>16)&0xFF,(ip>>8)&0xFF,ip&0xFF,path);
163
164 if (meta==0) {
165 p = path;
166 while (*p=='/') {
167 p++;
168 }
169 pleng = 0;
170 while (p[pleng]) {
171 pleng++;
172 }
173 while (pleng>0 && p[pleng-1]=='/') {
174 pleng--;
175 }
176 } else {
177 p = NULL;
178 pleng = 0;
179 }
180 rndstate=0;
181 if (rndcode!=NULL) {
182 for (i=0 ; i<32 ; i++) {
183 rndstate|=rndcode[i];
184 }
185 }
186 nopass=0;
187 f=NULL;
188 for (e=exports_records ; e ; e=e->next) {
189 ok = 0;
190 // syslog(LOG_NOTICE,"entry: network:%u.%u.%u.%u-%u.%u.%u.%u",(e->fromip>>24)&0xFF,(e->fromip>>16)&0xFF,(e->fromip>>8)&0xFF,e->fromip&0xFF,(e->toip>>24)&0xFF,(e->toip>>16)&0xFF,(e->toip>>8)&0xFF,e->toip&0xFF);
191 if (ip>=e->fromip && ip<=e->toip && version>=e->minversion && meta==e->meta) {
192 // syslog(LOG_NOTICE,"ip and version ok");
193 // path check
194 if (meta) { // no path in META
195 ok=1;
196 } else {
197 if (e->pleng==0) { // root dir
198 // syslog(LOG_NOTICE,"rootdir entry (pleng:%u)",pleng);
199 if (pleng==0) {
200 ok=1;
201 } else if (e->alldirs) {
202 ok=1;
203 }
204 } else {
205 // syslog(LOG_NOTICE,"entry path: %s (pleng:%u)",e->path,e->pleng);
206 if (pleng==e->pleng && memcmp(p,e->path,pleng)==0) {
207 ok=1;
208 } else if (e->alldirs && pleng>e->pleng && p[e->pleng]=='/' && memcmp(p,e->path,e->pleng)==0) {
209 ok=1;
210 }
211 }
212 }
213 // if (ok) {
214 // syslog(LOG_NOTICE,"path ok");
215 // }
216 if (ok && e->needpassword) {
217 if (rndstate==0 || rndcode==NULL || passcode==NULL) {
218 ok=0;
219 nopass=1;
220 } else {
221 md5_init(&md5c);
222 md5_update(&md5c,rndcode,16);
223 md5_update(&md5c,e->passworddigest,16);
224 md5_update(&md5c,rndcode+16,16);
225 md5_final(entrydigest,&md5c);
226 if (memcmp(entrydigest,passcode,16)!=0) {
227 ok=0;
228 nopass=1;
229 }
230 }
231 }
232 }
233 if (ok) {
234 // syslog(LOG_NOTICE,"entry accepted");
235 if (f==NULL) {
236 f=e;
237 } else {
238 if ((e->sesflags&SESFLAG_READONLY)==0 && (f->sesflags&SESFLAG_READONLY)!=0) { // prefer rw to ro
239 f=e;
240 } else if (e->rootuid==0 && f->rootuid!=0) { // prefer root not restricted to restricted
241 f=e;
242 } else if ((e->sesflags&SESFLAG_ADMIN)!=0 && (f->sesflags&SESFLAG_ADMIN)==0) { // prefer lines with more privileges
243 f=e;
244 } else if (e->needpassword==1 && f->needpassword==0) { // prefer lines with passwords
245 f=e;
246 } else if (e->pleng > f->pleng) { // prefer more accurate path
247 f=e;
248 }
249 }
250 }
251 }
252 if (f==NULL) {
253 if (nopass) {
254 if (rndstate==0 || rndcode==NULL || passcode==NULL) {
255 return ERROR_NOPASSWORD;
256 } else {
257 return ERROR_BADPASSWORD;
258 }
259 }
260 return ERROR_EACCES;
261 }
262 *sesflags = f->sesflags;
263 *rootuid = f->rootuid;
264 *rootgid = f->rootgid;
265 *mapalluid = f->mapalluid;
266 *mapallgid = f->mapallgid;
267 *mingoal = f->mingoal;
268 *maxgoal = f->maxgoal;
269 *mintrashtime = f->mintrashtime;
270 *maxtrashtime = f->maxtrashtime;
271 return STATUS_OK;
272 }
273
exports_freelist(exports * arec)274 void exports_freelist(exports *arec) {
275 exports *drec;
276 while (arec) {
277 drec = arec;
278 arec = arec->next;
279 if (drec->path) {
280 free((uint8_t *)(drec->path));
281 }
282 free(drec);
283 }
284 }
285
286 // format:
287 // ip[/bits] path options
288 //
289 // options:
290 // readonly
291 // maproot=uid[:gid]
292 // alldirs
293 // md5pass=md5(password)
294 // password=password
295 // dynamicip
296 // ignoregid
297 // admin
298 // mingoal=#
299 // maxgoal=#
300 // mintrashtime=[#w][#d][#h][#m][#[s]]
301 // maxtrashtime=[#w][#d][#h][#m][#[s]]
302 //
303 // ip[/bits] can be '*' (same as 0.0.0.0/0)
304 //
305 // default:
306 // * / alldirs,maproot=0
307
exports_parsenet(char * net,uint32_t * fromip,uint32_t * toip)308 int exports_parsenet(char *net,uint32_t *fromip,uint32_t *toip) {
309 uint32_t ip,i,octet;
310 if (net[0]=='*' && net[1]==0) {
311 *fromip = 0;
312 *toip = 0xFFFFFFFFU;
313 return 0;
314 }
315 ip=0;
316 for (i=0 ; i<4; i++) {
317 if (*net>='0' && *net<='9') {
318 octet=0;
319 while (*net>='0' && *net<='9') {
320 octet*=10;
321 octet+=*net-'0';
322 net++;
323 if (octet>255) {
324 return -1;
325 }
326 }
327 } else {
328 return -1;
329 }
330 if (i<3) {
331 if (*net!='.') {
332 return -1;
333 }
334 net++;
335 }
336 ip*=256;
337 ip+=octet;
338 }
339 if (*net==0) {
340 *fromip = ip;
341 *toip = ip;
342 return 0;
343 }
344 if (*net=='/') { // ip/bits and ip/mask
345 *fromip = ip;
346 ip=0;
347 net++;
348 for (i=0 ; i<4; i++) {
349 if (*net>='0' && *net<='9') {
350 octet=0;
351 while (*net>='0' && *net<='9') {
352 octet*=10;
353 octet+=*net-'0';
354 net++;
355 if (octet>255) {
356 return -1;
357 }
358 }
359 } else {
360 return -1;
361 }
362 if (i==0 && *net==0 && octet<=32) { // bits -> convert to mask and skip rest of loop
363 ip = 0xFFFFFFFF;
364 if (octet<32) {
365 ip<<=32-octet;
366 }
367 break;
368 }
369 if (i<3) {
370 if (*net!='.') {
371 return -1;
372 }
373 net++;
374 }
375 ip*=256;
376 ip+=octet;
377 }
378 if (*net!=0) {
379 return -1;
380 }
381 *fromip &= ip;
382 *toip = *fromip | (ip ^ 0xFFFFFFFFU);
383 return 0;
384 }
385 if (*net=='-') { // ip1-ip2
386 *fromip = ip;
387 ip=0;
388 net++;
389 for (i=0 ; i<4; i++) {
390 if (*net>='0' && *net<='9') {
391 octet=0;
392 while (*net>='0' && *net<='9') {
393 octet*=10;
394 octet+=*net-'0';
395 net++;
396 if (octet>255) {
397 return -1;
398 }
399 }
400 } else {
401 return -1;
402 }
403 if (i<3) {
404 if (*net!='.') {
405 return -1;
406 }
407 net++;
408 }
409 ip*=256;
410 ip+=octet;
411 }
412 if (*net!=0) {
413 return -1;
414 }
415 *toip = ip;
416 return 0;
417 }
418 return -1;
419 }
420
exports_parsegoal(char * goalstr,uint8_t * goal)421 int exports_parsegoal(char *goalstr,uint8_t *goal) {
422 if (*goalstr<'1' || *goalstr>'9' || *(goalstr+1)) {
423 return -1;
424 }
425 *goal = *goalstr-'0';
426 return 0;
427 }
428
429 // # | [#w][#d][#h][#m][#s]
exports_parsetime(char * timestr,uint32_t * time)430 int exports_parsetime(char *timestr,uint32_t *time) {
431 uint64_t t;
432 uint64_t tp;
433 uint8_t bits;
434 char *p;
435 for (p=timestr ; *p ; p++) {
436 if (*p>='a' && *p<='z') {
437 *p -= ('a'-'A');
438 }
439 }
440 t = 0;
441 bits = 0;
442 while (1) {
443 if (*timestr<'0' || *timestr>'9') {
444 return -1;
445 }
446 tp = 0;
447 while (*timestr>='0' && *timestr<='9') {
448 tp *= 10;
449 tp += *timestr-'0';
450 timestr++;
451 if (tp>UINT64_C(0x100000000)) {
452 return -1;
453 }
454 }
455 switch (*timestr) {
456 case 'W':
457 if (bits&0x1F) {
458 return -1;
459 }
460 bits |= 0x10;
461 tp *= 604800;
462 timestr++;
463 break;
464 case 'D':
465 if (bits&0x0F) {
466 return -1;
467 }
468 bits |= 0x08;
469 tp *= 86400;
470 timestr++;
471 break;
472 case 'H':
473 if (bits&0x07) {
474 return -1;
475 }
476 bits |= 0x04;
477 tp *= 3600;
478 timestr++;
479 break;
480 case 'M':
481 if (bits&0x03) {
482 return -1;
483 }
484 bits |= 0x02;
485 tp *= 60;
486 timestr++;
487 break;
488 case 'S':
489 if (bits&0x01) {
490 return -1;
491 }
492 bits |= 0x01;
493 timestr++;
494 break;
495 case '\0':
496 if (bits) {
497 return -1;
498 }
499 break;
500 default:
501 return -1;
502 }
503 t += tp;
504 if (t>UINT64_C(0x100000000)) {
505 return -1;
506 }
507 if (*timestr=='\0') {
508 *time = t;
509 return 0;
510 }
511 }
512 return -1; // unreachable
513 }
514
515 // x | x.y | x.y.z -> ( x<<16 + y<<8 + z )
exports_parseversion(char * verstr,uint32_t * version)516 int exports_parseversion(char *verstr,uint32_t *version) {
517 uint32_t vp;
518 if (*verstr<'0' || *verstr>'9') {
519 return -1;
520 }
521 vp=0;
522 while (*verstr>='0' && *verstr<='9') {
523 vp*=10;
524 vp+=*verstr-'0';
525 verstr++;
526 }
527 if (vp>255 || (*verstr!='.' && *verstr)) {
528 return -1;
529 }
530 *version = vp<<16;
531 if (*verstr==0) {
532 return 0;
533 }
534 verstr++;
535 if (*verstr<'0' || *verstr>'9') {
536 return -1;
537 }
538 vp=0;
539 while (*verstr>='0' && *verstr<='9') {
540 vp*=10;
541 vp+=*verstr-'0';
542 verstr++;
543 }
544 if (vp>255 || (*verstr!='.' && *verstr)) {
545 return -1;
546 }
547 *version += vp<<8;
548 if (*verstr==0) {
549 return 0;
550 }
551 verstr++;
552 if (*verstr<'0' || *verstr>'9') {
553 return -1;
554 }
555 vp=0;
556 while (*verstr>='0' && *verstr<='9') {
557 vp*=10;
558 vp+=*verstr-'0';
559 verstr++;
560 }
561 if (vp>255 || *verstr) {
562 return -1;
563 }
564 *version += vp;
565 return 0;
566 }
567
exports_parseuidgid(char * maproot,uint32_t lineno,uint32_t * ruid,uint32_t * rgid)568 int exports_parseuidgid(char *maproot,uint32_t lineno,uint32_t *ruid,uint32_t *rgid) {
569 char *uptr,*gptr,*eptr;
570 struct group *grrec,grp;
571 struct passwd *pwrec,pwd;
572 char pwgrbuff[16384];
573 uint32_t uid,gid;
574 int gidok;
575
576 uptr = maproot;
577 gptr = maproot;
578 while (*gptr && *gptr!=':') {
579 gptr++;
580 }
581
582 if (*gptr==':') {
583 *gptr = 0;
584 gid = 0;
585 eptr = gptr+1;
586 while (*eptr>='0' && *eptr<='9') {
587 gid*=10;
588 gid+=*eptr-'0';
589 eptr++;
590 }
591 if (*eptr!=0) { // not only digits - treat it as a groupname
592 getgrnam_r(gptr+1,&grp,pwgrbuff,16384,&grrec);
593 // grrec = getgrnam(gptr+1);
594 if (grrec==NULL) {
595 mfs_arg_syslog(LOG_WARNING,"mfsexports/maproot: can't find group named '%s' defined in line: %"PRIu32,gptr+1,lineno);
596 return -1;
597 }
598 gid = grrec->gr_gid;
599 }
600 gidok = 1;
601 } else {
602 gidok = 0;
603 gid = 0;
604 gptr = NULL;
605 }
606
607 uid = 0;
608 eptr = uptr;
609 while (*eptr>='0' && *eptr<='9') {
610 uid*=10;
611 uid+=*eptr-'0';
612 eptr++;
613 }
614 if (*eptr!=0) { // not only digits - treat it as a username
615 getpwnam_r(uptr,&pwd,pwgrbuff,16384,&pwrec);
616 // pwrec = getpwnam(uptr);
617 if (pwrec==NULL) {
618 mfs_arg_syslog(LOG_WARNING,"mfsexports/maproot: can't find user named '%s' defined in line: %"PRIu32,uptr,lineno);
619 return -1;
620 }
621 *ruid = pwrec->pw_uid;
622 if (gidok==0) {
623 *rgid = pwrec->pw_gid;
624 } else {
625 *rgid = gid;
626 }
627 return 0;
628 } else if (gidok==1) {
629 *ruid = uid;
630 *rgid = gid;
631 return 0;
632 } else {
633 getpwuid_r(uid,&pwd,pwgrbuff,16384,&pwrec);
634 // pwrec = getpwuid(uid);
635 if (pwrec==NULL) {
636 mfs_arg_syslog(LOG_WARNING,"mfsexports/maproot: can't determine gid, because can't find user with uid %"PRIu32" defined in line: %"PRIu32,uid,lineno);
637 return -1;
638 }
639 *ruid = pwrec->pw_uid;
640 *rgid = pwrec->pw_gid;
641 return 0;
642 }
643 return -1; // unreachable
644 }
645
exports_parseoptions(char * opts,uint32_t lineno,exports * arec)646 int exports_parseoptions(char *opts,uint32_t lineno,exports *arec) {
647 char *p;
648 int o;
649 md5ctx ctx;
650
651 while ((p=exports_strsep(&opts,","))) {
652 o=0;
653 // syslog(LOG_WARNING,"option: %s",p);
654 switch (*p) {
655 case 'r':
656 if (strcmp(p,"ro")==0) {
657 arec->sesflags |= SESFLAG_READONLY;
658 o=1;
659 } else if (strcmp(p,"readonly")==0) {
660 arec->sesflags |= SESFLAG_READONLY;
661 o=1;
662 } else if (strcmp(p,"rw")==0) {
663 arec->sesflags &= ~SESFLAG_READONLY;
664 o=1;
665 } else if (strcmp(p,"readwrite")==0) {
666 arec->sesflags &= ~SESFLAG_READONLY;
667 o=1;
668 }
669 break;
670 case 'i':
671 if (strcmp(p,"ignoregid")==0) {
672 if (arec->meta) {
673 mfs_arg_syslog(LOG_WARNING,"meta option ignored: %s",p);
674 } else {
675 arec->sesflags |= SESFLAG_IGNOREGID;
676 }
677 o=1;
678 }
679 break;
680 case 'a':
681 if (strcmp(p,"alldirs")==0) {
682 if (arec->meta) {
683 mfs_arg_syslog(LOG_WARNING,"meta option ignored: %s",p);
684 } else {
685 arec->alldirs = 1;
686 }
687 o=1;
688 } else if (strcmp(p,"admin")==0) {
689 if (arec->meta) {
690 mfs_arg_syslog(LOG_WARNING,"meta option ignored: %s",p);
691 } else {
692 arec->sesflags |= SESFLAG_ADMIN;
693 }
694 o=1;
695 }
696 break;
697 case 'd':
698 if (strcmp(p,"dynamicip")==0) {
699 arec->sesflags |= SESFLAG_DYNAMICIP;
700 o=1;
701 }
702 break;
703 case 'c':
704 if (strcmp(p,"canchangequota")==0) { // deprecated - use 'admin'
705 if (arec->meta) {
706 mfs_arg_syslog(LOG_WARNING,"meta option ignored: %s",p);
707 } else {
708 arec->sesflags |= SESFLAG_ADMIN;
709 }
710 o=1;
711 }
712 break;
713 case 'm':
714 if (strncmp(p,"maproot=",8)==0) {
715 o=1;
716 if (arec->meta) {
717 mfs_arg_syslog(LOG_WARNING,"meta option ignored: %s",p);
718 } else {
719 if (exports_parseuidgid(p+8,lineno,&arec->rootuid,&arec->rootgid)<0) {
720 return -1;
721 }
722 arec->rootredefined = 1;
723 }
724 } else if (strncmp(p,"mapall=",7)==0) {
725 o=1;
726 if (arec->meta) {
727 mfs_arg_syslog(LOG_WARNING,"meta option ignored: %s",p);
728 } else {
729 if (exports_parseuidgid(p+7,lineno,&arec->mapalluid,&arec->mapallgid)<0) {
730 return -1;
731 }
732 arec->sesflags |= SESFLAG_MAPALL;
733 }
734 } else if (strncmp(p,"md5pass=",8)==0) {
735 char *ptr = p+8;
736 uint32_t i=0;
737 o=1;
738 while ((*ptr>='0' && *ptr<='9') || (*ptr>='a' && *ptr<='f') || (*ptr>='A' && *ptr<='F')) {
739 ptr++;
740 i++;
741 }
742 if (*ptr==0 && i==32) {
743 ptr = p+8;
744 for (i=0 ; i<16 ; i++) {
745 if (*ptr>='0' && *ptr<='9') {
746 arec->passworddigest[i]=(*ptr-'0')<<4;
747 } else if (*ptr>='a' && *ptr<='f') {
748 arec->passworddigest[i]=(*ptr-'a'+10)<<4;
749 } else {
750 arec->passworddigest[i]=(*ptr-'A'+10)<<4;
751 }
752 ptr++;
753 if (*ptr>='0' && *ptr<='9') {
754 arec->passworddigest[i]+=(*ptr-'0');
755 } else if (*ptr>='a' && *ptr<='f') {
756 arec->passworddigest[i]+=(*ptr-'a'+10);
757 } else {
758 arec->passworddigest[i]+=(*ptr-'A'+10);
759 }
760 ptr++;
761 }
762 arec->needpassword=1;
763 } else {
764 mfs_arg_syslog(LOG_WARNING,"mfsexports: incorrect md5pass definition (%s) in line: %"PRIu32,p,lineno);
765 return -1;
766 }
767 } else if (strncmp(p,"minversion=",11)==0) {
768 o=1;
769 if (exports_parseversion(p+11,&arec->minversion)<0) {
770 mfs_arg_syslog(LOG_WARNING,"mfsexports: incorrect minversion definition (%s) in line: %"PRIu32,p,lineno);
771 return -1;
772 }
773 } else if (strncmp(p,"mingoal=",8)==0) {
774 o=1;
775 if (exports_parsegoal(p+8,&arec->mingoal)<0) {
776 mfs_arg_syslog(LOG_WARNING,"mfsexports: incorrect mingoal definition (%s) in line: %"PRIu32,p,lineno);
777 return -1;
778 }
779 if (arec->mingoal>arec->maxgoal) {
780 mfs_arg_syslog(LOG_WARNING,"mfsexports: mingoal>maxgoal in definition (%s) in line: %"PRIu32,p,lineno);
781 return -1;
782 }
783 } else if (strncmp(p,"maxgoal=",8)==0) {
784 o=1;
785 if (exports_parsegoal(p+8,&arec->maxgoal)<0) {
786 mfs_arg_syslog(LOG_WARNING,"mfsexports: incorrect maxgoal definition (%s) in line: %"PRIu32,p,lineno);
787 return -1;
788 }
789 if (arec->mingoal>arec->maxgoal) {
790 mfs_arg_syslog(LOG_WARNING,"mfsexports: maxgoal<mingoal in definition (%s) in line: %"PRIu32,p,lineno);
791 return -1;
792 }
793 } else if (strncmp(p,"mintrashtime=",13)==0) {
794 o=1;
795 if (exports_parsetime(p+13,&arec->mintrashtime)<0) {
796 mfs_arg_syslog(LOG_WARNING,"mfsexports: incorrect mintrashtime definition (%s) in line: %"PRIu32,p,lineno);
797 return -1;
798 }
799 if (arec->mintrashtime>arec->maxtrashtime) {
800 mfs_arg_syslog(LOG_WARNING,"mfsexports: mintrashtime>maxtrashtime in definition (%s) in line: %"PRIu32,p,lineno);
801 return -1;
802 }
803 } else if (strncmp(p,"maxtrashtime=",13)==0) {
804 o=1;
805 if (exports_parsetime(p+13,&arec->maxtrashtime)<0) {
806 mfs_arg_syslog(LOG_WARNING,"mfsexports: incorrect maxtrashtime definition (%s) in line: %"PRIu32,p,lineno);
807 return -1;
808 }
809 if (arec->mintrashtime>arec->maxtrashtime) {
810 mfs_arg_syslog(LOG_WARNING,"mfsexports: maxtrashtime<mintrashtime in definition (%s) in line: %"PRIu32,p,lineno);
811 return -1;
812 }
813 }
814 break;
815 case 'p':
816 if (strncmp(p,"password=",9)==0) {
817 md5_init(&ctx);
818 md5_update(&ctx,(uint8_t*)(p+9),strlen(p+9));
819 md5_final(arec->passworddigest,&ctx);
820 arec->needpassword=1;
821 o=1;
822 }
823 break;
824 }
825 if (o==0) {
826 mfs_arg_syslog(LOG_WARNING,"mfsexports: unknown option '%s' in line: %"PRIu32" (ignored)",p,lineno);
827 }
828 }
829 return 0;
830 }
831
exports_parseline(char * line,uint32_t lineno,exports * arec)832 int exports_parseline(char *line,uint32_t lineno,exports *arec) {
833 char *net,*path;
834 char *p;
835 uint32_t pleng;
836
837 arec->pleng = 0;
838 arec->path = NULL;
839 arec->fromip = 0;
840 arec->toip = 0;
841 arec->minversion = 0;
842 arec->alldirs = 0;
843 arec->needpassword = 0;
844 arec->meta = 0;
845 arec->rootredefined = 0;
846 arec->sesflags = SESFLAG_READONLY;
847 arec->mingoal = 1;
848 arec->maxgoal = 9;
849 arec->mintrashtime = 0;
850 arec->maxtrashtime = UINT32_C(0xFFFFFFFF);
851 arec->rootuid = 999;
852 arec->rootgid = 999;
853 arec->mapalluid = 999;
854 arec->mapallgid = 999;
855 arec->next = NULL;
856
857 p = line;
858 while (*p==' ' || *p=='\t') {
859 p++;
860 }
861 if (*p==0 || *p=='#') { // empty line or line with comment only
862 return -1;
863 }
864 net = p;
865 while (*p && *p!=' ' && *p!='\t') {
866 p++;
867 }
868 if (*p==0) {
869 mfs_arg_syslog(LOG_WARNING,"mfsexports: incomplete definition in line: %"PRIu32,lineno);
870 return -1;
871 }
872 *p=0;
873 p++;
874 if (exports_parsenet(net,&arec->fromip,&arec->toip)<0) {
875 mfs_arg_syslog(LOG_WARNING,"mfsexports: incorrect ip/network definition in line: %"PRIu32,lineno);
876 return -1;
877 }
878
879 while (*p==' ' || *p=='\t') {
880 p++;
881 }
882 if (p[0]=='.' && (p[1]==0 || p[1]==' ' || p[1]=='\t')) {
883 path = NULL;
884 pleng = 0;
885 arec->rootuid = 0;
886 arec->rootgid = 0;
887 arec->meta = 1;
888 p++;
889 } else {
890 while (*p=='/') {
891 p++;
892 }
893 path = p;
894 pleng = 0;
895 while (*p && *p!=' ' && *p!='\t') {
896 p++;
897 pleng++;
898 }
899 while (pleng>0 && path[pleng-1]=='/') {
900 pleng--;
901 }
902 }
903 if (*p==0) {
904 // no options - use defaults
905 arec->pleng = pleng;
906 if (pleng>0) {
907 arec->path = malloc(pleng+1);
908 passert(arec->path);
909 memcpy((uint8_t*)(arec->path),path,pleng);
910 ((uint8_t*)(arec->path))[pleng]=0;
911 } else {
912 arec->path = NULL;
913 }
914
915 return 0;
916 }
917 while (*p==' ' || *p=='\t') {
918 p++;
919 }
920
921 if (exports_parseoptions(p,lineno,arec)<0) {
922 return -1;
923 }
924
925 if ((arec->sesflags&SESFLAG_MAPALL) && (arec->rootredefined==0)) {
926 arec->rootuid = arec->mapalluid;
927 arec->rootgid = arec->mapallgid;
928 }
929
930 arec->pleng = pleng;
931 if (pleng>0) {
932 arec->path = malloc(pleng+1);
933 passert(arec->path);
934 memcpy((uint8_t*)(arec->path),path,pleng);
935 ((uint8_t*)(arec->path))[pleng]=0;
936 } else {
937 arec->path = NULL;
938 }
939
940 return 0;
941 }
942
exports_loadexports(void)943 void exports_loadexports(void) {
944 FILE *fd;
945 char linebuff[10000];
946 uint32_t s,lineno;
947 exports *newexports,**netail,*arec;
948
949 fd = fopen(ExportsFileName,"r");
950 if (fd==NULL) {
951 if (errno==ENOENT) {
952 if (exports_records) {
953 syslog(LOG_WARNING,"mfsexports configuration file (%s) not found - exports not changed",ExportsFileName);
954 } else {
955 syslog(LOG_WARNING,"mfsexports configuration file (%s) not found - no exports !!!",ExportsFileName);
956 }
957 fprintf(stderr,"mfsexports configuration file (%s) not found - please create one (you can copy %s.dist to get a base configuration)\n",ExportsFileName,ExportsFileName);
958 } else {
959 if (exports_records) {
960 mfs_arg_errlog(LOG_WARNING,"can't open mfsexports configuration file (%s) - exports not changed, error",ExportsFileName);
961 } else {
962 mfs_arg_errlog(LOG_WARNING,"can't open mfsexports configuration file (%s) - no exports !!!, error",ExportsFileName);
963 }
964 }
965 return;
966 }
967 newexports = NULL;
968 netail = &newexports;
969 lineno = 1;
970 arec = malloc(sizeof(exports));
971 passert(arec);
972 while (fgets(linebuff,10000,fd)) {
973 linebuff[9999]=0;
974 s=strlen(linebuff);
975 while (s>0 && (linebuff[s-1]=='\r' || linebuff[s-1]=='\n' || linebuff[s-1]=='\t' || linebuff[s-1]==' ')) {
976 s--;
977 }
978 if (s>0) {
979 linebuff[s]=0;
980 if (exports_parseline(linebuff,lineno,arec)>=0) {
981 *netail = arec;
982 netail = &(arec->next);
983 arec = malloc(sizeof(exports));
984 passert(arec);
985 }
986 }
987 lineno++;
988 }
989 free(arec);
990 if (ferror(fd)) {
991 fclose(fd);
992 syslog(LOG_WARNING,"error reading mfsexports file - exports not changed");
993 exports_freelist(newexports);
994 fprintf(stderr,"error reading mfsexports file - using defaults\n");
995 return;
996 }
997 fclose(fd);
998 exports_freelist(exports_records);
999 exports_records = newexports;
1000 mfs_syslog(LOG_NOTICE,"exports file has been loaded");
1001 }
1002
exports_reload(void)1003 void exports_reload(void) {
1004 int fd;
1005 if (ExportsFileName) {
1006 free(ExportsFileName);
1007 }
1008 if (!cfg_isdefined("EXPORTS_FILENAME")) {
1009 ExportsFileName = strdup(ETC_PATH "/mfs/mfsexports.cfg");
1010 passert(ExportsFileName);
1011 if ((fd = open(ExportsFileName,O_RDONLY))<0 && errno==ENOENT) {
1012 free(ExportsFileName);
1013 ExportsFileName = strdup(ETC_PATH "/mfsexports.cfg");
1014 if ((fd = open(ExportsFileName,O_RDONLY))>=0) {
1015 mfs_syslog(LOG_WARNING,"default sysconf path has changed - please move mfsexports.cfg from "ETC_PATH"/ to "ETC_PATH"/mfs/");
1016 }
1017 }
1018 if (fd>=0) {
1019 close(fd);
1020 }
1021 } else {
1022 ExportsFileName = cfg_getstr("EXPORTS_FILENAME",ETC_PATH "/mfs/mfsexports.cfg");
1023 }
1024 exports_loadexports();
1025 }
1026
exports_term(void)1027 void exports_term(void) {
1028 exports_freelist(exports_records);
1029 if (ExportsFileName) {
1030 free(ExportsFileName);
1031 }
1032 }
1033
exports_init(void)1034 int exports_init(void) {
1035 exports_records = NULL;
1036 ExportsFileName = NULL;
1037 exports_reload();
1038 if (exports_records==NULL) {
1039 fprintf(stderr,"no exports defined !!!\n");
1040 return -1;
1041 }
1042 main_reload_register(exports_reload);
1043 main_destruct_register(exports_term);
1044 return 0;
1045 }
1046