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