blob: 0305b0a273bbc27b3869c9de542eceb1a5c994ab [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001#pragma strict_types, no_warn_deprecated
2
3#include "/secure/master.h"
4
5static mapping projects=([]);
6static string *insecure,*deputy_files;
7
8int ReloadInsecureFile()
9{
10 insecure = efun::explode( read_file("/secure/INSECURE") || "", "\n" );
11 insecure -= ({""});
12 insecure = map( insecure, #'lower_case/*'*/ );
13 return(1);
14}
15
16void LoadDeputyFileList()
17{
18 // Leseberechtigungen fuer /log/ARCH/* setzen
19 deputy_files = efun::explode( read_file("/log/ARCH/DEPUTY_FILELIST") || "",
20 "\n" ) - ({""});
21 return;
22}
23
24#define PATH_ARRAY(x) (explode(x, "/")-({"."}))
25string *full_path_array(string path, string user) {
26 string *strs;
27 string cwd;
28
29 if(!path)
30 return ({"",""}); // additional "" to yield "/" later.
31
32 // remove multiple '/'
33 path = regreplace(path, "/+", "/", 1);
34
35 switch(path[0]) {
36 case '/':
37 if(!path[1]) return ({"",""}); //additional "" to yield "/" later
38 strs=PATH_ARRAY(path);
39 if (!sizeof(strs))
40 strs = ({"",""});
41 break;
42 case '+':
43 if(!path[1]) return ({"","d"});
44 strs=({"","d"})+PATH_ARRAY(path[1..<1]);
45 break;
46 case '~':
47 if(user && !path[1])
48 return ({"","players",user});
49 if(user && path[1]=='/')
50 strs=({"","players",user})+PATH_ARRAY(path[2..<1]);
51 else
52 strs=({"","players"})+PATH_ARRAY(path[1..<1]);
53 break;
54 default:
55 if(user && TP && getuid(TP) == user
56 && (cwd=(string)TP->QueryProp(P_CURRENTDIR)))
57 strs=PATH_ARRAY(cwd + "/" + path);
58 else
59 strs=PATH_ARRAY(path);
60 }
61 // /../ behandeln. Einschraenkungen der RegExp:
62 // /a/b/../../d wuerde nur durch Wiederholung aufgeloest.
63 // /../d wird nicht richtig aufgeloest.
64 //regreplace("/d/inseln/toeter/room/hoehle/../wald/bla.c","(.*)\/[^/]+/\.\.
65 // /(.*)", "\\1\/\\2", RE_GLOBAL)
66
67 // dies sieht schlimmer aus als es ist (member ist O(n)), solange das Array
68 // nicht gross wird.
69 int p;
70 while((p=member(strs, "..")) != -1)
71 strs = strs[0..p-2]+strs[p+1..];
72
73 return strs;
74}
75#undef PATH_ARRAY
76
77string _get_path(string path, string user) {
78 string *p_arr = full_path_array(path, user);
79 // make path absolute
80 if (p_arr[0] != "")
81 p_arr = ({""}) + p_arr;
82
83 return implode(p_arr,"/");
84}
85
86static int project_access(string user, string project)
87{
88 mixed *lines;
89 string s;
90 mapping tmp;
91
92 if (!member(projects,project))
93 {
94 s=read_file(PROJECTDIR+"/"+project+"/ACCESS_RIGHTS");
95 if(!s||s=="")
96 return 0;
97 tmp=([]);
98 for (lines=explode(s,"\n")-({""});sizeof(lines);lines=lines[1..])
99 {
100 if (lines[0][0]=='*')
101 tmp[lines[0][1..]]=2;
102 else
103 tmp[lines[0]]=1;
104 }
105 projects[project]=({tmp,time()});
106 return tmp[user];
107 }
108 projects[project][1]=time();
109 return projects[project][0][user];
110}
111
112void OutdateProjectCache(string project)
113{
114 m_delete(projects,project);
115}
116
117static void _cleanup_projects() {
118 int i;
119 mixed *users;
120
121 for (users=m_indices(projects),i=sizeof(users)-1;i>=0;i--)
122 if((time()-projects[users[i]][1])>1800)
123 m_delete(projects,users[i]);
124}
125
126int access_rights(string *p_arr, string euid)
127{
128 int i;
129
130 i = sizeof(p_arr) - 2;
131
132 while ( i >= 0 &&
133 file_size( implode(p_arr[0..i], "/") + "/access_rights.c" ) < 0 )
134 i--;
135
136 if ( i < 0 )
137 return 0;
138
139 if ( !catch(i = (int)call_other(
140 implode(p_arr[0..i], "/") + "/access_rights",
141 "access_rights", euid,
142 implode(p_arr[i+1..], "/") ); publish) )
143 return i;
144
145 return 0;
146}
147
Zesstra425149d2018-11-05 22:29:32 +0100148// Diese Funktion wird vom Driver nur fuer den Editor ed gerufen.
MG Mud User88f12472016-06-24 23:31:02 +0200149string make_path_absolute(string str) {
150 return _get_path(str, getuid(TP));
151}
152
Zesstra078991b2013-01-26 11:56:45 +0100153// Hngl. Inkludieren aus /sys ist hier doof.
154#ifndef FSIZE_DIR
155#define FSIZE_DIR -2
156#endif
MG Mud User88f12472016-06-24 23:31:02 +0200157mixed valid_write(string path, string euid, string fun, object obj)
158{
159 int s,lvl;
160 string *strs;
161 int *date;
162
163 if (member(path,' ')!=-1) return 0;
164
165 // Unter LIBDATADIR (/data) sollen komplett identische Schreibrechte
Zesstra078991b2013-01-26 11:56:45 +0100166 // vergeben werden. Ausnahme ist jedoch fun=="mkdir": hier soll die
167 // Erstellung eines Verezeichnisses jedem erlaubt sein, wenn es das
168 // entsprechende Verzeichnis ausserhalb /data/ schon gibt.
MG Mud User88f12472016-06-24 23:31:02 +0200169 if (sizeof(path) > 6
Zesstraeb0ceb82018-02-28 21:31:07 +0100170 && path[0..5] == "/"LIBDATADIR"/")
171 {
Zesstra078991b2013-01-26 11:56:45 +0100172 if (fun=="mkdir")
Zesstraeb0ceb82018-02-28 21:31:07 +0100173 {
174 // wenn schon ausserhalb /data/ existent: erlauben
175 if (file_size(path[5..]) == FSIZE_DIR)
176 return 1;
177 // sonst fall-through und normale gucken, ob der Aufrufer ausserhalb
178 // /data/ denn duerfte.
179 }
MG Mud User88f12472016-06-24 23:31:02 +0200180 return valid_write(path[5..], euid, fun, obj) != 0;
Zesstra078991b2013-01-26 11:56:45 +0100181 }
MG Mud User88f12472016-06-24 23:31:02 +0200182
183 switch(fun) {
184 case "log_file":
185 strs=full_path_array("/"+path, 0);
186 path = implode(strs, "/");
187 strs -= ({""}); // remove trailing and leading "/".
188 if (sizeof(strs)>1 && strs[0]=="log") return path;
189 return 0;
190 case "save_object":
191 if (!path) return 0;
192 strs=full_path_array("/"+path, 0);
193 break;
194 case "ed_start":
195 if (path && path[0])
196 strs=full_path_array(path, euid);
197 else strs=({"players",euid,".err"});
198 break;
199 default:
200 strs=full_path_array(path, euid);
201 }
202
203 if (!euid || euid=="NOBODY" || euid=="ftp" || euid=="anonymous") return 0;
204
205 // Pfad soll ab jetzt auf jeden Fall absolut sein.
206 if (strs[0] != "")
207 path = "/" + implode(strs, "/");
208 else
209 path=implode(strs, "/");
210
211 // fuer die Rechtebestimmung "/" an Anfang und Ende entfernen
212 strs -= ({""});
213
214 //Root-Objekte und Master duerfen immer.
215 if (euid == ROOTID || obj==TO) return path;
216
217 lvl=query_wiz_level(euid);
218
219 //Toplevel: nur EM+
220 if ((s=sizeof(strs))<=1) return lvl>=ARCH_LVL;
221
222 switch(strs[0]) {
223 case TMPDIR:
224 return 1;
225
226 case STDDIR:
227 case SYSDIR:
228 case LIBOBJDIR:
229 case ETCDIR:
230 case LIBROOMDIR:
231 return lvl>=ARCH_LVL;
232
233 case LIBITEMDIR:
234 return lvl>=ELDER_LVL;
235
236 case SPELLBOOKDIR:
237 // wenn unterhalb eines unterverzeichnisses des Stils "gilde", dann ists
238 // einfach
239 if (sizeof(strs) > 2
240 && guild_master(euid, strs[1]))
241 return 1;
242
243 // sonst nur EMs bzw. access_rights.c fragen.
244 return ((lvl>=ARCH_LVL) || access_rights(strs,euid));
245
246 case GUILDDIR:
247 // wenn unterhalb eines unterverzeichnisses des Stils "filde.gilde",
248 // dann ists einfach
249 if (sizeof(strs) > 2) {
250 string *tmp = explode(strs[1],"files.");
251 if (sizeof(tmp) == 2) {
252 if (guild_master(euid, tmp[1]))
253 return 1;
254 // Objekte dort sollen auch schreiben duerfen.
255 if (euid == ("GUILD."+tmp[1]))
256 return 1;
257 }
258 }
259 // sonst nur EMs bzw. access_rights.c fragen.
260 return ((lvl>=ARCH_LVL) || access_rights(strs,euid));
261
262 case DOCDIR:
263 if ( s > 1 && (strs[1] == "balance") )
264 return ((lvl>=ARCH_LVL) || access_rights(strs,euid));
265 else
266 return ((lvl>=SPECIAL_LVL) || access_rights(strs,euid));
267
268 case FTPDIR:
269 if ( s > 1 && (strs[1] == "incoming" || strs[1] == "tmp" ||
270 s == 3 && strs[1] == "News" && strs[2] == euid+".news") )
271 return 1;
272
273 if (lvl>=ELDER_LVL)
274 return 1;
275
276 return 0;
277
278 case MAILDIR:
279 if (euid==MAILID || strs[1]=="spool") break;
280 return 0;
281
Zesstra8a3f8682017-01-30 23:04:20 +0100282 case "save":
MG Mud User88f12472016-06-24 23:31:02 +0200283 if (lvl>=ARCH_LVL) return 1;
284 if (s==3 && strs[1] == euid[0..0] &&
285 (strs[2]==euid+".o" || strs[2]==euid)) break;
286 return 0;
287
288 case LIBLOGDIR:
289 /* auf /log/ARCH/ haben wirklich nur EMs Zugriff */
290 if (strs[1]=="ARCH" && lvl<ARCH_LVL) return 0;
291
292 /* alles andere duerfen auch Weise... */
293 if (lvl>=ELDER_LVL) return 1;
294
295 /* Allgemeine logfiles in /log duerfen wirklich nur geschrieben werden */
296 if (s==2 && strs[1][0]>='A' && strs[1][0]<='Z' && fun != "write_file")
297 return 0;
298
299 /* Unterhalb von /log/syslog darf nur geschrieben werden */
300 if (s>1 && strs[1]=="syslog" && fun != "write_file")
301 return 0;
302
303 /* loggen ins eigene repfile immer erlauben */
304 if (s==3 && strs[1]=="report" &&
305 strs[2]==explode(euid, ".")[<1]+".rep") break;
306
307 /* in fremden Verzeichnissen hat niemand was zu loggen */
308 if (get_wiz_level(strs[1]) >= WIZARD_LVL &&
309 strs[1] != efun::explode(euid, ".")[<1])
310 return 0;
311 break;
312
313 case WIZARDDIR:
314 /* kein Zugriff auf Objekte mit hoeheren Rechten */
315 if (query_wiz_level(strs[1]) > lvl) return 0;
316
317 /* Magier selbst oder Weise duerfen hier schreiben */
318 if ((IS_WIZARD(euid) && euid==strs[1])||(lvl>=ELDER_LVL)) break;
319
320 /* Es steht jedoch frei, auch anderen Schreibrechte zu geben... */
321 return access_rights(strs,euid);
322
323 case DOMAINDIR:
324 /* neue Regionen duerfen nur Erzmagier anlegen */
325 if (s<2 && lvl<ARCH_LVL) return 0;
326
327 /* kein Zugriff auf Objekte mit hoeheren Rechten */
328 if (s>2 && query_wiz_level(creator_file(path)) > lvl)
329 return 0;
330
331 /* Auf Regionfiles von erzmagier haben nur EMs Zugriff */
332 if (creator_file(path)=="erzmagier" && lvl<ARCH_LVL) return 0;
333
334 /* innerhalb der Region haben RMs und Weise volle Schreibrechte */
335 if (lvl>=ELDER_LVL || domain_master(euid,strs[1])) break;
336
337 /* neue Verzeichnisse in der Region kann nur RM+ anlegen */
338 if (s<=3 && (fun=="mkdir" || fun=="rmdir")) return 0;
339
340 /* Magier auf ihre eigenen Files... */
341 if (s>2 && strs[2]==euid) break;
342
343 /* Files der Magier in der Region in ihre eigenen Verzeichnisse... */
344 if (s>2 && euid==sprintf("d.%s.%s", strs[1], strs[2])) break;
345
346 /* Files mit Regionsuid */
347 if (euid==strs[1]) break;
348
349 /* Es ist moeglich anderen Magiern Rechte bei sich einzuraeumen. */
350 if (access_rights(strs,euid)>0) break;
351
352 /* Auf das Verzeichniss 'alle' haben alle Regionsmitglieder Zugriff.
353 Ausser, wenn sie ueber access_rights.c explizit ausgeschlossen
354 werden (Returncode < 0). */
355 if (s>2 && strs[2]=="alle" && domain_member(euid, strs[1]) &&
356 access_rights(strs,euid)>=0) break;
357 return 0;
358
359 case PROJECTDIR:
360 /* Nur Erzmagier duerfen neue Projektverzeichnisse anlegen... */
361 if (s<3 && lvl<ARCH_LVL) return 0;
362
363 /* alles weitere duerfen auch Weise tun */
364 if (lvl>=ELDER_LVL) break;
365
366 /* in den Serviceverzeichnissen muss lediglich der Name stimmen */
367 if (s>3 && strs[1]=="service" && euid==strs[2]) break;
368
369 /* Objekte eines Projektes haben Schreibzugriffe auf ihr Projekt */
370 if (s>3 && (implode(strs[0..1], ".") == euid
371 || implode(strs[0..2], ".") == euid) ) return 1;
372
373 /* Schreibrechte koennen natuerlich auch vergeben werden... */
374 if (project_access(euid,strs[1])) break;
375 // Alternativ besteht neben dem ACCESS_RIGHTS auch noch die
376 // Moeglichkeit, per access_rights.c Rechte einzuraeumen.
377 if (access_rights(strs,euid)>0) break;
378
379 return 0;
380
381 default: return 0;
382 }
383 return path;
384}
385
386mixed valid_read(string path, string euid, string fun, object obj)
387{
388 int s, lev;
389 string *strs, suf;
390
391 if (member(path,' ')!=-1) return 0;
392
393 // Unter LIBDATADIR (/data) sollen komplett identische Leserechte
394 // vergeben werden.
395 if (sizeof(path) > 6
396 && path[0..5] == "/"LIBDATADIR"/")
397 return valid_read(path[5..], euid, fun, obj) != 0;
398
399 if (!euid) euid="-";
400
401 strs=full_path_array(path, euid);
402 // Pfad soll ab jetzt auf jeden Fall absolut sein.
403 if (strs[0] != "")
404 path = "/" + implode(strs, "/");
405 else
406 path=implode(strs, "/");
407
408 // fuer die Rechtebestimmung "/" an Anfang und Ende entfernen
409 strs -= ({""});
410
411 if (!sizeof(strs) || !sizeof(path) || euid == ROOTID || obj==TO) return path;
412
413 if ((s=sizeof(strs)) <= 1) return path;
414
415 lev=query_wiz_level(euid);
416
417 switch(strs[0]) {
418 case "core":
419 case "wahl":
420 return 0;
421
422 case NEWSDIR:
423 if (strs[1] == "archiv.pub") // oeffentliches archiv
424 return path;
425 else if (strs[1] == "archiv.magier" // Magier-Archiv
426 && lev >= WIZARD_LVL)
427 return path;
428
429 return 0; // kein Zugriff auf /news/ oder alles andere.
430
431 case "maps":
432 if (lev<=WIZARD_LVL) return 0;
433
434 case "":
435 case ETCDIR:
436 case STDDIR:
437 case SYSDIR:
438 case DOCDIR:
439 case LIBOBJDIR:
440 case LIBROOMDIR:
441 case LIBITEMDIR:
442 case FTPDIR:
443 return path;
444
445 case MAILDIR:
446 return (euid==MAILID);
447
448 case SECUREDIR:
449 if (strs[1]=="save") return 0;
450 if (path[<2..]==".o" || path[<5..]==".dump") return 0;
451 if (strs[1]!="ARCH" || lev>=ARCH_LVL) return path;
452
453 case LIBLOGDIR:
454 if ( strs[1] == "ARCH" && !IS_DEPUTY(euid) )
455 return 0;
456
457 if ( strs[1] == "ARCH" && lev < ARCH_LVL && s == 3 &&
458 strs[2] != "*" && strs[2][0..2] != "bb." &&
459 member( deputy_files, strs[2] ) < 0 )
460 return 0;
461
462 if ( lev > WIZARD_LVL )
463 return path;
464
465 if ( s == 2 && (strs[1] == "report" || strs[1] == "error") )
466 return path; // fuer 'cd'
467
468 // /log sollte jeder auflisten koennen
469 if ( s == 2 && strs[1]=="*" && fun=="get_dir")
470 return path;
471
472 // unter /log/report/, /log/error und /log/warning/ duerfen alle lesen
473 if ( s==3 &&
474 (strs[1] == "report" || strs[1] == "error"
475 || strs[1] =="warning"))
476 return path;
477
478 // /log/<Magiername> darf von <Magiername> natuerlich auch
479 // gelesen werden
480 if ( s >= 2 && strs[1] == euid )
481 return path;
482
483 return 0;
484
485 case "backup":
Zesstra8a3f8682017-01-30 23:04:20 +0100486 case "save":
MG Mud User88f12472016-06-24 23:31:02 +0200487 if (lev>WIZARD_LVL) return path;
488
489 /* Objekte in /p/* haben bisher leider wizlevel 0 */
490 //if (lev==0) return path;
491
492 if (fun=="file_size") return path;
493
494 // das eigene Savefile darf man natuerlich immer. ;-)
495 if (s==3 && (strs[2]==euid+".o" || strs[2]==euid))
496 return path;
497 return 0;
498
499 case PROJECTDIR:
500 /* Pruefen ob ein File existiert darf jeder... */
501 if (fun=="file_size") return path;
502
503 /* Die Service-Verzeichnisse darf jeder lesen */
504 if (s>3 && strs[1]=="service") return path;
505
506 //Weise duerfen in /p/ schreiben, also auch lesen. (Zesstra, 04.11.06)
507 if (lev>=ELDER_LVL) return path;
508
509 /* wer hier schreiben darf, darf natuerlich auf jeden Fall lesen */
510 //Anmerkung v. Zesstra: das prueft nur, ob jemand in ACCESS_RIGHTS
511 //steht, nicht ob jemand (ggf. aus anderen Gruenden schreiben darf)
512 if (project_access(euid,strs[1])) return path;
513 //Alternativ kann man explizite Schreibrechte auch via access_rights.c
514 //vergeben. (und damit natuerlich auch Leserechte)
515 if (access_rights(strs,euid)>0) return path;
516
517 /* Objekte eines Projektes haben Lesezugriff auf ihr Projekt */
518 if (s>3 && (implode(strs[0..1], ".") == euid
519 || implode(strs[0..2], ".") == euid) ) return path;
520
521 /* Fall-Through */
522
523 case GUILDDIR:
524 /* "secure"-Verzeichnisse darf nur lesen, wer da auch schreiben darf */
525 if ( s > 3 && strs[<2] == "secure" && member( insecure, strs[1] ) < 0
526 && lev < ARCH_LVL && !access_rights(strs, euid) )
527 return 0;
528
529 /* Pruefen ob ein File existiert darf jeder... */
530 if (fun=="file_size") return path;
531
532 /* Fall-Through */
533
534 case SPELLBOOKDIR:
535 // Gildenpbjekte koennen hier lesen
536 if (lev==0 && euid[0..4]=="GUILD") return path;
537
538 // Mit Level <= 20 darf man hier nichts lesen
539 if ( lev<=WIZARD_LVL && sizeof(regexp( ({strs[<1]}), "\\.[och]" )) )
540 return 0;
541
542 return path;
543
544 case WIZARDDIR:
545 if (s==2) return path;
546 /* das eigene Verzeichniss darf man natuerlich immer lesen... */
547 if (strs[1]==euid && lev>=WIZARD_LVL) return path;
548
549 /* Pruefen ob ein File existiert darf jeder... */
550 if (fun=="file_size") return path;
551
552 /* fremde Verzeichnisse mit <= Level 20 noch nicht */
553 if (lev<=WIZARD_LVL) return 0;
554
555 /* wo man schreiben darf, darf man natuerlich auch lesen... */
556 if (lev>=ELDER_LVL) return path;
557
558 // kein Zugriff auf archivierten Code (wo z.B. secure/ drin sein
559 // koennen)
560 if (member(({"bz2","gz","zip"}), explode(strs[<1],".")[<1]) > -1)
561 return 0;
562
563 /* Files ohne Code sind nicht weiter interessant... */
564 if ( !sizeof(regexp( ({strs[<1]}), "\\.[och]" )) )
565 return path;
566
567 /* folgende Funktionen sind natuerlich voellig uninteressant */
568 if (member(({"get_dir", "restore_object"}), fun)!=-1)
569 return path;
570
571 /* Zugriff erlaubt, aber mitloggen */
572 write_file( READ_FILE, sprintf("%O %s %s: %s\n", fun, ctime()[4..],
573 euid, path) );
574 return path;
575
576 case DOMAINDIR:
577 /* Mit Level 15 darf man hier _nichts_ lesen */
578 if ( fun!="file_size" && lev<WIZARD_LVL &&
579 sizeof(regexp( ({strs[<1]}), "\\.[och]" )) ) return 0;
580
581 /* den Verzeichnisbaum von /d/ darf man natuerlich sonst lesen */
582 if (s<=2) return path;
583
584 /* eigenen Code darf man natuerlich auch lesen */
585 if (euid==strs[2] || euid==sprintf("d.%s.%s", strs[1], strs[2]))
586 return path;
587
588 /* Deputies haben ein gemeinsames Verzeichnis unter /d/erzmagier */
589 if (strs[1]=="erzmagier" && strs[2]=="polizei" && IS_DEPUTY(euid))
590 return path;
591
592 /* d/erzmagier darf man nur als Erzmagier lesen */
593 if (strs[1]=="erzmagier" && lev<ARCH_LVL) return 0;
594
595 /* einige Regionen haben std-verzeichnisse, die darf man natuerlich
596 auch mit Level 20 bereits komplett lesen! */
597 if (strs[2]=="std") return path;
598
599 /* "secure"-Verzeichnisse darf nur lesen, wer da auch schreiben darf */
600 if ( s > 4 && strs[<2] == "secure" && member( insecure, strs[2] ) < 0 ){
601 if ( lev < ELDER_LVL && !domain_master(euid, strs[1])
602 && !access_rights(strs, euid) )
603 return 0;
604 else
605 return path;
606 }
607
608 /* Dokus, Infos und .readme darf man immer lesen... */
609 if ( fun=="file_size" || !sizeof(regexp( ({strs[<1]}), "\\.[och]" )) )
610 return path;
611
612 /* Mit Level 20 darf man noch keinen fremden Code lesen! */
613 if (lev<=WIZARD_LVL) return 0;
614
615 /* Weise duerfen natuerlich alles weitere lesen */
616 if (lev>=ELDER_LVL) return path;
617
618 /* Regionsmagier in ihren Regionen natuerlich auch */
619 if (lev>=LORD_LVL && domain_master(euid, strs[1])) return path;
620
621 /* folgende Funktionen sind natuerlich voellig uninteressant */
622 if (member(({"get_dir", "restore_object"}), fun)!=-1)
623 return path;
624
625 /* Zugriff erlaubt, aber mitloggen */
626 write_file( READ_FILE, sprintf("%O %s %s: %s\n", fun, ctime()[4..],
627 euid, path) );
628 return path;
629 }
630 if (lev<ARCH_LVL) return 0;
631 return path;
632}
633