1 /*
2  **      $Id: policy.c 1061 2009-04-06 12:48:10Z aaron $
3  */
4 /************************************************************************
5  *                                                                      *
6  *                             Copyright (C)  2003                      *
7  *                                Internet2                             *
8  *                             All Rights Reserved                      *
9  *                                                                      *
10  ************************************************************************/
11 /*
12  **        File:            policy.c
13  **
14  **        Author:          Jeff W. Boote
15  **
16  **        Date:            Mon Jan 20 10:42:57 MST 2003
17  **
18  **        Description:
19  **      Default policy  functions used by OWAMP applications.
20  */
21 #include <owamp/owamp.h>
22 
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <ctype.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <limits.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <netinet/in.h>
33 #include <assert.h>
34 
35 #include "policy.h"
36 #include "fts.h"
37 
38 /*
39  * Function:        parsepfs
40  *
41  * Description:
42  *                 Read all pass-phrases from the pfsfile and populate the pfs
43  *                 hash with that data.
44  *
45  * In Args:
46  *
47  * Out Args:
48  *
49  * Scope:
50  * Returns:
51  * Side Effect:
52  */
53 static int
parsepfs(OWPDPolicy policy,FILE * fp,char ** lbuf,size_t * lbuf_max)54 parsepfs(
55         OWPDPolicy  policy,
56         FILE        *fp,
57         char        **lbuf,
58         size_t      *lbuf_max
59         )
60 {
61     int         rc=0;
62     char        *username;
63     char        *passphrase;
64     size_t      pf_len;
65     I2Datum     key,val;
66     I2ErrHandle eh = OWPContextErrHandle(policy->ctx);
67 
68     if(!fp){
69         return 0;
70     }
71 
72     while((rc = I2ParsePFFile(eh,fp,NULL,rc,
73                     NULL,
74                     &username,
75                     &passphrase,
76                     &pf_len,
77                     lbuf,lbuf_max)) > 0){
78 
79         /*
80          * Make sure the username is not already in the hash.
81          */
82         key.dptr = username;
83         key.dsize = strlen(username);
84         if(I2HashFetch(policy->pfs,key,&val)){
85             OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
86                     "username \"%s\" duplicated",username);
87             return -rc;
88         }
89 
90         /*
91          * alloc memory for the username key.
92          */
93         if(!(key.dptr = strdup(username))){
94             OWPError(policy->ctx,OWPErrFATAL,errno,
95                     "strdup(username): %M");
96             return -rc;
97         }
98 
99         /*
100          * alloc memory for pass-phrase value.
101          */
102         if(!(val.dptr = malloc(pf_len))){
103             free(key.dptr);
104             OWPError(policy->ctx,OWPErrFATAL,errno,
105                     "malloc(len(pass-phrase)): %M");
106             return -rc;
107         }
108         memcpy(val.dptr,passphrase,pf_len);
109         val.dsize = pf_len;
110 
111         if(I2HashStore(policy->pfs,key,val) != 0){
112             free(key.dptr);
113             free(val.dptr);
114             OWPError(policy->ctx,OWPErrFATAL,OWPErrUNKNOWN,
115                     "Unable to store pass-phrase for %s",
116                     username);
117             return -rc;
118         }
119     }
120 
121     return rc;
122 }
123 
124 enum limtype{LIMINTVAL,LIMBOOLVAL,LIMNOT};
125 struct limdesc{
126     OWPDMesgT       limit;
127     char            *lname;
128     enum limtype    ltype;
129     OWPBoolean      release_on_exit;
130     OWPDLimitT      def_value;
131 };
132 
133 static struct limdesc        limkeys[] = {
134 {OWPDLimParent,         "parent",           LIMNOT,     0,  0},
135 {OWPDLimBandwidth,      "bandwidth",        LIMINTVAL,  1,  0},
136 {OWPDLimDisk,           "disk",             LIMINTVAL,  0,  0},
137 {OWPDLimDeleteOnFetch,  "delete_on_fetch",  LIMBOOLVAL, 0,  0},
138 {OWPDLimAllowOpenMode,  "allow_open_mode",  LIMBOOLVAL, 0,  1}
139 };
140 
141 static OWPDLimitT
GetDefLimit(OWPDMesgT lim)142 GetDefLimit(
143         OWPDMesgT   lim
144         )
145 {
146     size_t  i;
147 
148     for(i=0;i<I2Number(limkeys);i++){
149         if(lim == limkeys[i].limit){
150             return limkeys[i].def_value;
151         }
152     }
153 
154     return 0;
155 }
156 
157 static char *
GetLimName(OWPDMesgT lim)158 GetLimName(
159         OWPDMesgT   lim
160         )
161 {
162     size_t        i;
163 
164     for(i=0;i<I2Number(limkeys);i++){
165         if(lim == limkeys[i].limit){
166             return limkeys[i].lname;
167         }
168     }
169 
170     return "unknown";
171 }
172 
173 static int
parselimitline(OWPDPolicy policy,char * line,size_t maxlim)174 parselimitline(
175         OWPDPolicy  policy,
176         char        *line,
177         size_t      maxlim
178         )
179 {
180     size_t              i,j;
181     char                *cname;
182     OWPDLimRec          limtemp[I2Number(limkeys)];
183     OWPDPolicyNodeRec   tnode;
184     OWPDPolicyNode      node;
185     I2Datum             key,val;
186 
187     /*
188      * Grab new classname
189      */
190     if(!(line = strtok(line,I2WSPACESET))){
191         return 1;
192     }
193     cname = line;
194 
195     /*
196      * verify classname has not been defined before.
197      */
198     key.dptr = cname;
199     key.dsize = strlen(cname);
200     if(key.dsize > OWPDMAXCLASSLEN){
201         OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
202                 "classname \"%s\" too long - max length = %u",cname,
203                 OWPDMAXCLASSLEN);
204         return 1;
205     }
206     if(I2HashFetch(policy->limits,key,&val)){
207         OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
208                 "classname \"%s\" duplicated",cname);
209         return 1;
210     }
211 
212     /*
213      * parse "with"
214      */
215     if(!(line = strtok(NULL,I2WSPACESET))){
216         return 1;
217     }
218     /* compare strings INCLUDING the '\0' */
219     if(strncasecmp(line,"with",5)){
220         return 1;
221     }
222 
223     memset(&tnode,0,sizeof(tnode));
224     memset(limtemp,0,sizeof(limtemp));
225 
226     tnode.policy = policy;
227 
228     /*
229      * Process key/value pairs delimited by ','
230      */
231     while((line = strtok(NULL,","))){
232         char                *limname,*limval;
233         OWPBoolean        found;
234 
235         if(tnode.ilim >= maxlim){
236             OWPError(policy->ctx,OWPErrFATAL,
237                     OWPErrINVALID,
238                     "Too many limit declarations");
239             return 1;
240         }
241 
242         /*
243          * Grab the keyname off the front.
244          */
245         while(isspace((int)*line)){line++;}
246         limname = line;
247         while(!isspace((int)*line) && (*line != '=')){
248             line++;
249         }
250         *line++ = '\0';
251 
252         /*
253          * Grab the valname
254          */
255         while(isspace((int)*line) || (*line == '=')){
256             line++;
257         }
258         limval = line;
259         while(!isspace((int)*line) && (*line != '\0')){
260             line++;
261         }
262         *line = '\0';
263 
264         if(!strncasecmp(limname,"parent",7)){
265             if(!policy->root){
266                 OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
267                         "\"parent\" specified for root node.");
268                 return 1;
269             }
270             if(tnode.parent){
271                 OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
272                         "multiple parents specified.");
273                 return 1;
274             }
275 
276             /* validate and fetch parent */
277             key.dptr = limval;
278             key.dsize = strlen(limval);
279             if(!I2HashFetch(policy->limits,key,&val)){
280                 OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
281                         "parent \"%s\" undefined",limval);
282                 return 1;
283             }
284             tnode.parent = val.dptr;
285             continue;
286         }
287 
288         found = False;
289         for(i=0;i < I2Number(limkeys);i++){
290             /* skip "special" limit types */
291             if(limkeys[i].ltype == LIMNOT){
292                 continue;
293             }
294 
295             /* skip non-matching limit names */
296             if(strncasecmp(limname,limkeys[i].lname,
297                         strlen(limkeys[i].lname)+1)){
298                 continue;
299             }
300 
301             /* i now points at correct record in limkeys */
302             found=True;
303             break;
304         }
305 
306         if(!found){
307             OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
308                     "Unknown limit name \"%s\".",limname);
309             return 1;
310         }
311 
312         /* check for a multiple definition */
313         for(j=0;j<tnode.ilim;j++){
314             if(limtemp[j].limit == limkeys[i].limit){
315                 OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
316                         "multiple %s values specified.",
317                         limname);
318                 return 1;
319             }
320         }
321 
322         /*
323          * Set the next record in limtemp with this limname/limvalue.
324          */
325         limtemp[tnode.ilim].limit = limkeys[i].limit;
326         switch(limkeys[i].ltype){
327 
328             case LIMINTVAL:
329                 if(I2StrToNum(&limtemp[tnode.ilim].value,limval)){
330                     OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
331                             "Invalid value specified for \"%s\".",
332                             limname);
333                     return 1;
334                 }
335                 break;
336             case LIMBOOLVAL:
337                 if(!strncasecmp(limval,"on",3)){
338                     limtemp[tnode.ilim].value = 1;
339                 }else if(strncasecmp(limval,"off",4)){
340                     OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
341                             "Invalid value specified for \"%s\".",
342                             limname);
343                     return 1;
344                 }
345                 break;
346             default:
347                 /* NOTREACHED */
348                 OWPError(policy->ctx,OWPErrFATAL,OWPErrUNKNOWN,
349                         "limkeys array is invalid!");
350         }
351 
352         tnode.ilim++;
353     }
354 
355     /*
356      * Now copy the parent parameters that were not overridden.
357      */
358     if(tnode.parent){
359         for(i=0;i<tnode.parent->ilim;i++){
360             for(j=0;j<tnode.ilim;j++){
361                 if(tnode.parent->limits[i].limit ==
362                         limtemp[j].limit){
363                     goto override;
364                 }
365             }
366             limtemp[tnode.ilim++] = tnode.parent->limits[i];
367 override:
368             ;
369         }
370     }
371     /*
372      * No parent - if root has been set, this is invalid.
373      */
374     else if(policy->root){
375         OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
376                 "\"parent\" must be specified for non-root node");
377         return 1;
378     }
379 
380     /*
381      * Now alloc memory and insert this node into the hash.
382      */
383     if(!(node = malloc(sizeof(*node))) ||
384             !(tnode.nodename = strdup(cname)) ||
385             !(tnode.limits = calloc(maxlim,sizeof(OWPDLimRec))) ||
386             !(tnode.used = calloc(maxlim,sizeof(OWPDLimRec)))){
387         OWPError(policy->ctx,OWPErrFATAL,errno,"alloc(): %M");
388         return 1;
389     }
390     memcpy(node,&tnode,sizeof(*node));
391     if(tnode.ilim){
392         memcpy(node->limits,limtemp,sizeof(OWPDLimRec)*tnode.ilim);
393         memcpy(node->used,limtemp,sizeof(OWPDLimRec)*tnode.ilim);
394         for(i=0;i<tnode.ilim;i++){
395             node->used[i].value = 0;
396         }
397     }
398 
399     key.dptr = node->nodename;
400     key.dsize = strlen(node->nodename);
401     val.dptr = node;
402     val.dsize = sizeof(*node);
403     if(I2HashStore(policy->limits,key,val) != 0){
404         OWPError(policy->ctx,OWPErrFATAL,OWPErrUNKNOWN,
405                 "Unable to store limit description!");
406         return 1;
407     }
408 
409     if(!policy->root){
410         policy->root = node;
411     }
412 
413     return 0;
414 }
415 
416 static int
parseassignline(OWPDPolicy policy,char * line)417 parseassignline(
418         OWPDPolicy  policy,
419         char        *line
420         )
421 {
422     OWPDPidRec  tpid;
423     OWPDPid     pid;
424     I2Datum     key,val;
425 
426     memset(&tpid,0,sizeof(tpid));
427 
428     /*
429      * Grab assign "type"
430      */
431     if(!(line = strtok(line,I2WSPACESET))){
432         return 1;
433     }
434 
435     if(!strncasecmp(line,"default",8)){
436         tpid.id_type = OWPDPidDefaultType;
437         key.dptr = &tpid;
438         key.dsize = sizeof(tpid);
439         if(I2HashFetch(policy->idents,key,&val)){
440             OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
441                     "Invalid multiple \"assign default\" lines.");
442             return 1;
443         }
444     }
445     else if(!strncasecmp(line,"net",4)){
446         int                tint;
447         char                *mask, *end;
448         struct addrinfo        hints, *res;
449         uint8_t        nbytes,nbits,*ptr;
450 
451         tpid.id_type = OWPDPidNetmaskType;
452         /*
453          * Grab addr/mask
454          */
455         if(!(line = strtok(NULL,I2WSPACESET))){
456             OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
457                     "Invalid \"assign net\" argument.");
458             return 1;
459         }
460 
461         if((mask = strchr(line,'/'))){
462             *mask++ = '\0';
463             if(*mask == '\0'){
464                 OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
465                         "Invalid address mask.");
466                 return 1;
467             }
468         }
469 
470         memset(&hints,0,sizeof(hints));
471         hints.ai_flags = AI_NUMERICHOST;
472         hints.ai_family = PF_UNSPEC;
473         hints.ai_socktype= SOCK_STREAM;
474         res = NULL;
475 
476         if((tint = getaddrinfo(line,NULL,&hints,&res)) < 0){
477             OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
478                     "Invalid address \"%s\": %s",line,
479                     gai_strerror(tint));
480             return 1;
481         }
482         else if(!res){
483             OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
484                     "Invalid address \"%s\".",line);
485             return 1;
486         }
487 
488         switch(res->ai_family){
489             struct sockaddr_in        *saddr4;
490 #ifdef        AF_INET6
491             struct sockaddr_in6        *saddr6;
492 
493             case AF_INET6:
494             saddr6 = (struct sockaddr_in6*)res->ai_addr;
495             tpid.net.addrsize = 16;
496             memcpy(tpid.net.addrval,saddr6->sin6_addr.s6_addr,16);
497             break;
498 #endif
499             case AF_INET:
500             saddr4 = (struct sockaddr_in*)res->ai_addr;
501             tpid.net.addrsize = 4;
502             memcpy(tpid.net.addrval,&saddr4->sin_addr.s_addr,4);
503             break;
504 
505             default:
506             freeaddrinfo(res);
507             OWPError(policy->ctx,OWPErrFATAL,OWPErrUNKNOWN,
508                     "Unknown address protocol family.");
509             return 1;
510             break;
511         }
512         freeaddrinfo(res);
513         res = NULL;
514 
515         if(mask){
516             unsigned long tlng;
517 
518             tlng = (int)strtoul(mask,&end,10);
519             if((*end != '\0') || (tlng < 1) ||
520                     (tlng > (tpid.net.addrsize*8))){
521                 OWPError(policy->ctx,OWPErrFATAL,OWPErrUNKNOWN,
522                         "Invalid address mask \"%s\".",mask);
523                 return 1;
524             }
525             tpid.net.mask_len = tlng;
526         }
527         else{
528             tpid.net.mask_len = tpid.net.addrsize*8;
529         }
530 
531         /*
532          * ensure addr part of addr/mask doesn't set any bits.
533          */
534 
535         nbytes = tpid.net.mask_len/8;
536         nbits = tpid.net.mask_len%8;
537         ptr = &tpid.net.addrval[nbytes];
538 
539         /*
540          * Check bits in byte following last complete one.
541          */
542         if(nbytes < tpid.net.addrsize){
543             if(*ptr & ~(0xFF << (8-nbits))){
544                 OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
545                         "Invalid address/mask combination.");
546                 return 1;
547             }
548         }
549 
550         /*
551          * Check remaining bytes following the partial one.
552          */
553         nbytes++;
554         ptr++;
555         while(nbytes < tpid.net.addrsize){
556             if(*ptr){
557                 OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
558                         "Invalid address/mask combination.");
559                 return 1;
560             }
561             nbytes++;
562             ptr++;
563         }
564     }
565     else if(!strncasecmp(line,"user",5)){
566         /*
567          * Grab username
568          */
569         if(!(line = strtok(NULL,I2WSPACESET))){
570             return 1;
571         }
572         key.dptr = line;
573         key.dsize = strlen(line);
574 
575         if((key.dsize >= sizeof(tpid.user.userid)) ||
576                 !I2HashFetch(policy->pfs,key,&val)){
577             OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
578                     "Invalid user \"%s\".",line);
579             return 1;
580         }
581 
582         tpid.id_type = OWPDPidUserType;
583         strcpy(tpid.user.userid,line);
584     }
585     else{
586         OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
587                 "Unknown \"assign\" specification.");
588         return 1;
589     }
590 
591     /*
592      * The Pid is valid - now parse and check for limits for
593      * the "classname".
594      */
595     if(!(line = strtok(NULL,I2WSPACESET))){
596         return 1;
597     }
598 
599     key.dptr = line;
600     key.dsize = strlen(line);
601     if(!I2HashFetch(policy->limits,key,&val)){
602         OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
603                 "Unknown limitclass name \"%s\".",line);
604         return 1;
605     }
606 
607     if(!(pid = malloc(sizeof(*pid)))){
608         OWPError(policy->ctx,OWPErrFATAL,errno,
609                 "malloc(OWPDPidRec): %M");
610         return 1;
611     }
612     memcpy(pid,&tpid,sizeof(*pid));
613     key.dptr = pid;
614     key.dsize = sizeof(*pid);
615     if(I2HashStore(policy->idents,key,val) != 0){
616         OWPError(policy->ctx,OWPErrFATAL,OWPErrUNKNOWN,
617                 "Unable to store assign description!");
618         return 1;
619     }
620 
621     return 0;
622 }
623 
624 static int
parselimits(OWPDPolicy policy,FILE * fp,char ** lbuf,size_t * lbuf_max)625 parselimits(
626         OWPDPolicy  policy,
627         FILE        *fp,
628         char        **lbuf,
629         size_t      *lbuf_max
630         )
631 {
632     int         rc = 0;
633     size_t      i;
634     size_t      maxlim = 0;
635     char        *line;
636     I2ErrHandle eh = OWPContextErrHandle(policy->ctx);
637 
638     /*
639      * Count number of possible limit parameters
640      */
641     for(i=0;i < I2Number(limkeys);i++){
642         if(limkeys[i].ltype != LIMNOT){
643             maxlim++;
644         }
645     }
646 
647     /*
648      * parse the file, one line at a time.
649      */
650     while(fp && ((rc = I2GetConfLine(eh,fp,rc,lbuf,lbuf_max)) > 0)){
651         line = *lbuf;
652 
653         /*
654          * parse limit lines. (These create the "user classes" and
655          * specify the "authorization" level of that authenticated
656          * "user class".
657          */
658         if(!strncasecmp(line,"limit",5)){
659             line += 5;
660             while(isspace((int)*line)){
661                 line++;
662             }
663 
664             if(parselimitline(policy,line,maxlim) != 0){
665                 return -rc;
666             }
667         }
668         /*
669          * parse "assign" lines. These are used to determine the
670          * identity of a connection. i.e. authenticate a particular
671          * connection as a particular identity/user class.
672          */
673         else if(!strncasecmp(line,"assign",6)){
674             line += 6;
675             while(isspace((int)*line)){
676                 line++;
677             }
678 
679             if(parseassignline(policy,line) != 0){
680                 return -rc;
681             }
682         }
683         else{
684             rc = -rc;
685             break;
686         }
687     }
688 
689     /*
690      * Add a "default" class if none was specified.
691      */
692     if((rc == 0) && !policy->root){
693         char        defline[] = "default with";
694 
695         OWPError(policy->ctx,OWPErrWARNING,OWPErrUNKNOWN,
696                 "WARNING: No limits specified.");
697 
698         line = *lbuf;
699         if(sizeof(defline) > *lbuf_max){
700             *lbuf_max += I2LINEBUFINC;
701             *lbuf = realloc(line,sizeof(char) * *lbuf_max);
702             if(!*lbuf){
703                 if(line){
704                     free(line);
705                 }
706                 OWPError(policy->ctx,OWPErrFATAL,errno,
707                         "realloc(%u): %M",*lbuf_max);
708                 return -1;
709             }
710             line = *lbuf;
711         }
712         strcpy(line,defline);
713         if(parselimitline(policy,line,maxlim) != 0){
714             OWPError(policy->ctx,OWPErrFATAL,OWPErrUNKNOWN,
715                     "Unable to install default (open) limits");
716             return -1;
717         }
718     }
719 
720 
721     return rc;
722 }
723 
724 /*
725  * Function:        node_dir
726  *
727  * Description:
728  *         This function creates a directory hierarchy based at datadir
729  *         equivalent to the "class" hierarchy reference by node.
730  *         i.e. It traverses up the "node" to determine all the parent nodes
731  *         that should be above it and uses the node names to create directory
732  *         names.
733  *
734  *         The "memory" record is PATH_MAX+1 bytes long - add_chars is used
735  *         to keep track of the number of bytes that are needed "after" this
736  *         node in the recursion to allow for graceful failure.
737  *
738  *
739  * In Args:
740  *
741  * Out Args:
742  *
743  * Scope:
744  * Returns:
745  * Side Effect:
746  */
747 static char *
node_dir(OWPContext ctx,OWPBoolean make,char * datadir,OWPDPolicyNode node,unsigned int add_chars,char * memory)748 node_dir(
749         OWPContext      ctx,
750         OWPBoolean      make,
751         char            *datadir,
752         OWPDPolicyNode  node,
753         unsigned int    add_chars,
754         char            *memory
755         )
756 {
757     char    *path;
758     int     len;
759 
760     if(node){
761         path = node_dir(ctx,make,datadir,node->parent,
762                 strlen(node->nodename) +
763                 OWP_PATH_SEPARATOR_LEN + add_chars, memory);
764         if(!path)
765             return NULL;
766         strcat(path,OWP_PATH_SEPARATOR);
767         strcat(path,node->nodename);
768     }
769     else {
770         len = strlen(datadir) + OWP_PATH_SEPARATOR_LEN
771             + strlen(OWP_HIER_DIR) + add_chars;
772         if(len > PATH_MAX){
773             OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
774                     "Data file path length too long.");
775             return NULL;
776         }
777         path = memory;
778 
779         strcpy(path,datadir);
780         strcat(path,OWP_PATH_SEPARATOR);
781         strcat(path, OWP_HIER_DIR);
782     }
783 
784     if(make && (mkdir(path,0755) != 0) && (errno != EEXIST)){
785         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
786                 "Unable to mkdir(%s): %M",path);
787         return NULL;
788     }
789 
790     return path;
791 }
792 
793 static OWPBoolean
clean_catalog(OWPContext ctx,char * path)794 clean_catalog(
795         OWPContext  ctx,
796         char        *path
797         )
798 {
799     char        *ftsargv[2];
800     FTS         *fts;
801     FTSENT      *p;
802     OWPBoolean  ret=False;
803 
804     ftsargv[0] = path;
805     ftsargv[1] = NULL;
806 
807     /*
808      * Make sure catalog dir exists.
809      */
810     if((mkdir(path,0755) != 0) && (errno != EEXIST)){
811         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
812                 "Unable to mkdir(%s): %M",path);
813         return False;
814     }
815 
816     if(!(fts = fts_open(ftsargv, FTS_PHYSICAL,NULL))){
817         OWPError(ctx,OWPErrFATAL,errno,"fts_open(%s): %M",path);
818         return False;
819     }
820 
821     while((p = fts_read(fts)) != NULL){
822         switch(p->fts_info){
823             case FTS_D:        /* ignore */
824             case FTS_DC:
825                 break;
826             case FTS_ERR:
827                 if(p->fts_errno != ENOENT){
828                     OWPError(ctx,OWPErrFATAL,p->fts_errno,"%s: %M",
829                             p->fts_path);
830                     goto err;
831                 }
832                 break;
833             case FTS_DNR:
834             case FTS_DP:
835                 /*
836                  * Keep the catalog dir itself.
837                  */
838                 if(p->fts_level < 1){
839                     break;
840                 }
841                 /*
842                  * Shouldn't really be any directories in here...
843                  * But delete any that show up.
844                  */
845                 if(rmdir(p->fts_accpath) && (errno != ENOENT)){
846                     OWPError(ctx,OWPErrFATAL,errno,"rmdir(%s): %M",
847                             p->fts_path);
848                     goto err;
849                 }
850                 break;
851             default:
852                 if(unlink(p->fts_accpath) && (errno != ENOENT)){
853                     OWPError(ctx,OWPErrFATAL,errno,"unlink(%s): %M",
854                             p->fts_path);
855                     goto err;
856                 }
857                 break;
858         }
859     }
860 
861     ret = True;
862 err:
863     fts_close(fts);
864 
865     return ret;
866 }
867 
868 static void
869 OWPDResourceUsage(
870         OWPDPolicyNode  node,
871         OWPDLimRec      lim
872         );
873 
874 static OWPBoolean
verify_datadir(OWPDPolicy policy,char * cpath,char * npath)875 verify_datadir(
876         OWPDPolicy  policy,
877         char        *cpath, /* catalog  */
878         char        *npath  /* nodes    */
879         )
880 {
881     char            *ftsargv[2];
882     FTS             *fts;
883     FTSENT          *p;
884     OWPBoolean      ret=False;
885     I2Datum         key,val;
886     OWPDPolicyNode  node;
887     char            pathname[PATH_MAX+1];
888     OWPDLimRec      lim;
889     OWPSID          tsid;
890     size_t          len;
891 
892     ftsargv[0] = npath;
893     ftsargv[1] = NULL;
894 
895     lim.limit = OWPDLimDisk;
896 
897     /*
898      * Need FTS_NOCHDIR because symlink could be created from
899      * a relative path. (i.e. if datadir is not set, it is relative
900      * to the current directory of the owampd process.)
901      */
902     if(!(fts = fts_open(ftsargv, FTS_NOCHDIR|FTS_PHYSICAL,NULL))){
903         if(errno == ENOENT){
904             return True;
905         }
906         OWPError(policy->ctx,OWPErrFATAL,errno,"fts_open(%s): %M",
907                 npath);
908         return False;
909     }
910 
911     while((p = fts_read(fts)) != NULL){
912         switch(p->fts_info){
913             case FTS_D:
914                 /*
915                  * pre-order directory. Find "node" and verify
916                  * parent.
917                  */
918 
919                 /*
920                  * ignore "nodes" directory and fts "root".
921                  */
922                 if(p->fts_level <= 0){
923                     break;
924                 }
925 
926                 key.dptr = p->fts_name;
927                 key.dsize = p->fts_namelen;
928                 if(!I2HashFetch(policy->limits,key,&val)){
929                     OWPError(policy->ctx,OWPErrWARNING,OWPErrPOLICY,
930                             "verify_datadir: Ignoring \"%s\": "
931                             "No associated user class",p->fts_path);
932                     fts_set(fts,p,FTS_SKIP);
933                     break;
934                 }
935                 node = val.dptr;
936 
937                 /*
938                  * verify node is in the correct hierarchy.
939                  * (It is either the root at level 0 - or the parent
940                  * found via fts must equal the node->parent.)
941                  */
942                 if(((p->fts_level == 1) && (policy->root == node)) ||
943                         ((p->fts_level > 1) &&
944                          (p->fts_parent->fts_pointer ==
945                           node->parent))){
946                     p->fts_pointer = node;
947                     break;
948                 }
949 
950                 OWPError(policy->ctx,OWPErrFATAL,OWPErrPOLICY,
951                         "verify_datadir: Directory \"%s\" "
952                         "expect at \"%s\"",
953                         p->fts_path,
954                         (node_dir(policy->ctx,False,
955                                   policy->datadir,node,0,
956                                   pathname))?pathname:"unknown");
957                 goto err;
958                 break;
959 
960             case FTS_DC:        /* ignore */
961                 break;
962             case FTS_DNR:
963             case FTS_ERR:
964                 if(p->fts_errno != ENOENT){
965                     OWPError(policy->ctx,OWPErrFATAL,p->fts_errno,
966                             "%s: %M",p->fts_path);
967                     goto err;
968                 }
969                 break;
970             case FTS_DP:
971                 /*
972                  * We should have skipped any directory entries
973                  * that don't coorespond to nodes - but check just
974                  * in case.
975                  */
976                 if(!p->fts_pointer){
977                     break;
978                 }
979                 node = p->fts_pointer;
980 
981                 /*
982                  * Now - place the "usage" for this level in the
983                  * node's disk usage pointer.
984                  * convert from st_blocks to bytes
985                  */
986                 lim.value = node->initdisk;
987                 OWPDResourceUsage((OWPDPolicyNode)p->fts_pointer,lim);
988 
989                 /*
990                  * Add disk space from this level to parent.
991                  */
992                 if(node->parent){
993                     node->parent->initdisk += node->initdisk;
994                 }
995 
996                 break;
997 
998             default:
999                 /*
1000                  * First - make sure this file is in a node managed
1001                  * directory.
1002                  */
1003                 if(!p->fts_parent->fts_pointer){
1004                     break;
1005                 }
1006                 node = p->fts_parent->fts_pointer;
1007 
1008                 /*
1009                  * Now make sure this file is a "session" file.
1010                  * (Is the length correct, does the suffix match,
1011                  * and are the first 32 charactors 16 hex encoded
1012                  * bytes?)
1013                  */
1014                 len = strlen(OWP_FILE_EXT);
1015                 if(((len + (sizeof(OWPSID)*2)) != p->fts_namelen) ||
1016                         strncmp(&p->fts_name[sizeof(OWPSID)*2],
1017                             OWP_FILE_EXT,len+1) ||
1018                         !I2HexDecode(p->fts_name,tsid,
1019                             sizeof(tsid))){
1020                     break;
1021                 }
1022 
1023                 /*
1024                  * build symlink in catalog to this file.
1025                  */
1026                 strcpy(pathname,cpath);
1027                 strcat(pathname,OWP_PATH_SEPARATOR);
1028                 strcat(pathname,p->fts_name);
1029                 if(symlink(p->fts_path,pathname) != 0){
1030                     OWPError(policy->ctx,OWPErrFATAL,errno,
1031                             "symlink(%s,%s): %M",
1032                             p->fts_path,pathname);
1033                     goto err;
1034                 }
1035 
1036                 /*
1037                  * Add size of this file to node.
1038                  */
1039                 node->initdisk += p->fts_statp->st_size;
1040 
1041                 break;
1042         }
1043     }
1044 
1045     ret = True;
1046 err:
1047     fts_close(fts);
1048 
1049     return ret;
1050 }
1051 
1052 static OWPBoolean
InitializeDiskUsage(OWPDPolicy policy)1053 InitializeDiskUsage(
1054         OWPDPolicy  policy
1055         )
1056 {
1057     char    cpath[PATH_MAX+1];
1058     char    npath[PATH_MAX+1];
1059     size_t  len1,len2;
1060 
1061     /*
1062      * Verify length of "catalog" symlink pathnames.
1063      * {datadir}/{OWP_CATALOG_DIR}/{SIDHEXNAME}{OWP_FILE_EXT}
1064      *
1065      * Verify length of the root of the "nodes" directory.
1066      * {datadir}/{OWP_HIER_DIR} - individual node paths will be
1067      * verified as the node hierarchy is validated and the catalog
1068      * is rebuilt.
1069      */
1070     len1 = strlen(policy->datadir) + OWP_PATH_SEPARATOR_LEN*2 +
1071         strlen(OWP_CATALOG_DIR) + sizeof(OWPSID)*2 +
1072         strlen(OWP_FILE_EXT);
1073     len2 = strlen(policy->datadir) + OWP_PATH_SEPARATOR_LEN +
1074         strlen(OWP_HIER_DIR);
1075     if(MAX(len1,len2) > PATH_MAX){
1076         OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
1077                 "InitializeDiskUsage: datadir too long (%s)",
1078                 policy->datadir);
1079         return False;
1080     }
1081 
1082     /*
1083      * verify datadir exists!
1084      */
1085     if((strlen(policy->datadir) > 0) &&
1086             (mkdir(policy->datadir,0755) != 0) &&
1087             (errno != EEXIST)){
1088         OWPError(policy->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1089                 "Unable to mkdir(%s): %M",policy->datadir);
1090         return False;
1091     }
1092 
1093     /*
1094      * Clean the catalog out. It is recreated each time owampd is
1095      * re-initialized.
1096      */
1097     strcpy(cpath,policy->datadir);
1098     strcat(cpath,OWP_PATH_SEPARATOR);
1099     strcat(cpath,OWP_CATALOG_DIR);
1100 
1101     if(!clean_catalog(policy->ctx,cpath)){
1102         OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
1103                 "InitializeDiskUsage: Invalid catalog directory: %s",
1104                 cpath);
1105         return False;
1106     }
1107 
1108     /*
1109      * Verify the datadir hierarchy - this determines the current disk
1110      * usage of each user-class and rebuilds the catalog.
1111      */
1112     strcpy(npath,policy->datadir);
1113     strcat(npath,OWP_PATH_SEPARATOR);
1114     strcat(npath,OWP_HIER_DIR);
1115 
1116     if(!verify_datadir(policy,cpath,npath)){
1117         OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
1118                 "InitializeDiskUsage: Invalid datadir directory: %s",
1119                 policy->datadir);
1120         return False;
1121     }
1122 
1123     return True;
1124 }
1125 
1126 /*
1127  * Function:        OWPDPolicyInstall
1128  *
1129  * Description:
1130  *         This function installs the functions defined in this file as
1131  *         the "policy" hooks within the owamp application.
1132  *
1133  *         The main reason for defining the policy in the owamp library
1134  *         like this was that it made it possible to share the policy
1135  *         code between client/server applications such as owping and
1136  *         owampd. Also, it is a good example of how this can be done for
1137  *         custom appliations (such as powstream).
1138  *
1139  * In Args:
1140  *
1141  * Out Args:
1142  *
1143  * Scope:
1144  * Returns:
1145  * Side Effect:
1146  *         This function does no clean-up of memory it allocates in the event
1147  *         of failure. It is expected that the application will report
1148  *         an error and exit if this function fails.
1149  *
1150  *         TODO: I really should fix this - it is lazy, and makes looking for
1151  *         memory leaks more difficult.
1152  */
1153 OWPDPolicy
OWPDPolicyInstall(OWPContext ctx,char * datadir,char * confdir,double diskfudge,char ** lbuf,size_t * lbuf_max)1154 OWPDPolicyInstall(
1155         OWPContext  ctx,
1156         char        *datadir,
1157         char        *confdir,
1158         double      diskfudge,
1159         char        **lbuf,
1160         size_t      *lbuf_max
1161         )
1162 {
1163     OWPDPolicy  policy;
1164     I2ErrHandle eh;
1165     char        pfname[MAXPATHLEN+1];
1166     char        lfname[MAXPATHLEN+1];
1167     int         len;
1168     FILE        *kfp,*lfp;
1169     int         rc;        /* row count */
1170 
1171     /*
1172      * use variables for the func pointers so the compiler can give
1173      * type-mismatch warnings.
1174      */
1175     OWPGetPFFunc                getpf = OWPDGetPF;
1176     OWPCheckControlPolicyFunc   checkcontrolfunc = OWPDCheckControlPolicy;
1177     OWPCheckTestPolicyFunc      checktestfunc = OWPDCheckTestPolicy;
1178     OWPCheckFetchPolicyFunc     checkfetchfunc = OWPDCheckFetchPolicy;
1179     OWPTestCompleteFunc         testcompletefunc = OWPDTestComplete;
1180     OWPOpenFileFunc             openfilefunc = OWPDOpenFile;
1181     OWPCloseFileFunc            closefilefunc = OWPDCloseFile;
1182 
1183 
1184     eh = OWPContextErrHandle(ctx);
1185 
1186     /*
1187      * Alloc main policy record
1188      */
1189     if(!(policy = calloc(1,sizeof(*policy)))){
1190         OWPError(ctx,OWPErrFATAL,errno,"calloc(policy rec): %M");
1191         return NULL;
1192     }
1193 
1194     policy->ctx = ctx;
1195     policy->diskfudge = diskfudge;
1196 
1197     /*
1198      * copy datadir
1199      */
1200     if(!datadir){
1201         datadir = ".";
1202     }
1203     if(!(policy->datadir = strdup(datadir))){
1204         OWPError(ctx,OWPErrFATAL,errno,"strdup(datadir): %M");
1205         return NULL;
1206     }
1207 
1208     /*
1209      * Alloc hashes.
1210      */
1211     if(!(policy->limits = I2HashInit(eh,0,NULL,NULL)) ||
1212             !(policy->idents =
1213                 I2HashInit(eh,0,NULL,NULL)) ||
1214             !(policy->pfs = I2HashInit(eh,0,NULL,NULL))){
1215         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
1216                 "OWPDPolicyInstall: Unable to allocate hashes");
1217         return NULL;
1218     }
1219 
1220     /*
1221      * Open the pass-phrase file.
1222      */
1223     pfname[0] = '\0';
1224     len = strlen(OWP_PFS_FILE);
1225     if(len > MAXPATHLEN){
1226         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
1227                 "strlen(OWP_PFS_FILE > MAXPATHLEN)");
1228         return NULL;
1229     }
1230 
1231     len += strlen(confdir) + strlen(OWP_PATH_SEPARATOR);
1232     if(len > MAXPATHLEN){
1233         OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
1234                 "Path to %s > MAXPATHLEN",OWP_PFS_FILE);
1235         return NULL;
1236     }
1237     strcpy(pfname,confdir);
1238     strcat(pfname,OWP_PATH_SEPARATOR);
1239     strcat(pfname,OWP_PFS_FILE);
1240     if(!(kfp = fopen(pfname,"r")) && (errno != ENOENT)){
1241         OWPError(ctx,OWPErrFATAL,errno,"Unable to open %s: %M",pfname);
1242         return NULL;
1243     }
1244 
1245     /*
1246      * Open the limits file.
1247      */
1248     lfname[0] = '\0';
1249     len = strlen(OWP_LIMITS_FILE);
1250     if(len > MAXPATHLEN){
1251         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
1252                 "strlen(OWP_LIMITS_FILE > MAXPATHLEN)");
1253         return NULL;
1254     }
1255 
1256     len += strlen(confdir) + strlen(OWP_PATH_SEPARATOR);
1257     if(len > MAXPATHLEN){
1258         OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
1259                 "Path to %s > MAXPATHLEN",OWP_LIMITS_FILE);
1260         return NULL;
1261     }
1262     strcpy(lfname,confdir);
1263     strcat(lfname,OWP_PATH_SEPARATOR);
1264     strcat(lfname,OWP_LIMITS_FILE);
1265 
1266     if(!(lfp = fopen(lfname,"r"))){
1267         if(errno != ENOENT){
1268             OWPError(ctx,OWPErrFATAL,errno,"Unable to open %s: %M",
1269                     lfname);
1270             return NULL;
1271         }
1272     }
1273 
1274     /*
1275      * lbuf is a char buffer that grows as needed in I2GetConfLine
1276      * lbuf will be realloc'd repeatedly as needed. Once conf file
1277      * parsing is complete - it is free'd from this function.
1278      */
1279     if((rc = parsepfs(policy,kfp,lbuf,lbuf_max)) < 0){
1280         OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
1281                 "%s:%d Invalid file syntax",pfname,-rc);
1282         return NULL;
1283     }
1284 
1285     if((rc = parselimits(policy,lfp,lbuf,lbuf_max)) < 0){
1286         OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
1287                 "%s:%d Invalid file syntax",lfname,-rc);
1288         return NULL;
1289     }
1290 
1291     if(kfp && (fclose(kfp) != 0)){
1292         OWPError(ctx,OWPErrFATAL,errno,"fclose(%s): %M",pfname);
1293         return NULL;
1294     }
1295 
1296     if(lfp && (fclose(lfp) != 0)){
1297         OWPError(ctx,OWPErrFATAL,errno,"fclose(%s): %M",lfname);
1298         return NULL;
1299     }
1300 
1301     /*
1302      * Policy files were parsed and loaded ok. Now, install policy
1303      * hook functions that will use it.
1304      *
1305      * Use func pointers to ensure we have functions of the correct
1306      * type.
1307      */
1308 
1309     if(!OWPContextConfigSetV(ctx,OWPDPOLICY,policy)){
1310         return NULL;
1311     }
1312     if(!OWPContextConfigSetF(ctx,OWPGetPF,(OWPFunc)getpf)){
1313         return NULL;
1314     }
1315     if(!OWPContextConfigSetF(ctx,OWPCheckControlPolicy,
1316                 (OWPFunc)checkcontrolfunc)){
1317         return NULL;
1318     }
1319     if(!OWPContextConfigSetF(ctx,OWPCheckTestPolicy,
1320                 (OWPFunc)checktestfunc)){
1321         return NULL;
1322     }
1323     if(!OWPContextConfigSetF(ctx,OWPCheckFetchPolicy,
1324                 (OWPFunc)checkfetchfunc)){
1325         return NULL;
1326     }
1327     if(!OWPContextConfigSetF(ctx,OWPTestComplete,
1328                 (OWPFunc)testcompletefunc)){
1329         return NULL;
1330     }
1331     if(!OWPContextConfigSetF(ctx,OWPOpenFile,(OWPFunc)openfilefunc)){
1332         return NULL;
1333     }
1334     if(!OWPContextConfigSetF(ctx,OWPCloseFile,(OWPFunc)closefilefunc)){
1335         return NULL;
1336     }
1337 
1338     return policy;
1339 }
1340 
1341 OWPBoolean
OWPDPolicyPostInstall(OWPDPolicy policy)1342 OWPDPolicyPostInstall(
1343     OWPDPolicy  policy
1344         )
1345 {
1346     /*
1347      * Now that the "user class" hierarchy is loaded - take a look
1348      * at datadir and initialize disk usage.
1349      */
1350     if(!InitializeDiskUsage(policy)){
1351         return False;
1352     }
1353 
1354     return True;
1355 }
1356 
1357 /*
1358  * Function:        OWPDGetPF
1359  *
1360  * Description:
1361  *         Fetch the 128 bit AES key for a given userid and return it.
1362  *
1363  *         Returns True if successful.
1364  *         If False is returned err_ret can be checked to determine if
1365  *         the key store had a problem(ErrFATAL) or if the userid is
1366  *         invalid(ErrOK).
1367  *
1368  * In Args:
1369  *
1370  * Out Args:
1371  *
1372  * Scope:
1373  * Returns:        T/F
1374  * Side Effect:
1375  */
1376 extern OWPBoolean
OWPDGetPF(OWPContext ctx,const OWPUserID userid,uint8_t ** pf,size_t * pf_len,void ** pf_free,OWPErrSeverity * err_ret)1377 OWPDGetPF(
1378         OWPContext      ctx,
1379         const OWPUserID userid,
1380         uint8_t         **pf,
1381         size_t          *pf_len,
1382         void            **pf_free,
1383         OWPErrSeverity  *err_ret
1384         )
1385 {
1386     OWPDPolicy  policy;
1387     I2Datum     key,val;
1388 
1389     *err_ret = OWPErrOK;
1390 
1391     if(!(policy = (OWPDPolicy)OWPContextConfigGetV(ctx,OWPDPOLICY))){
1392         OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
1393                 "OWPDGetPF: OWPDPOLICY not set");
1394         *err_ret = OWPErrFATAL;
1395         return False;
1396     }
1397 
1398     key.dptr = (void*)userid;
1399     key.dsize = strlen(userid);
1400     if(!I2HashFetch(policy->pfs,key,&val)){
1401         OWPError(policy->ctx,OWPErrFATAL,OWPErrPOLICY,
1402                 "userid \"%s\" unknown",userid);
1403         return False;
1404     }
1405 
1406     /* just point directly at memory in store */
1407     *pf = val.dptr;
1408     *pf_len = val.dsize;
1409     *pf_free = NULL;
1410 
1411     return True;
1412 }
1413 
1414 static OWPDPolicyNode
GetNodeDefault(OWPDPolicy policy)1415 GetNodeDefault(
1416         OWPDPolicy  policy
1417         )
1418 {
1419     OWPDPidRec  tpid;
1420     I2Datum     key,val;
1421 
1422     memset(&tpid,0,sizeof(tpid));
1423 
1424     tpid.id_type = OWPDPidDefaultType;
1425     key.dptr = &tpid;
1426     key.dsize = sizeof(tpid);
1427     if(I2HashFetch(policy->idents,key,&val)){
1428         return (OWPDPolicyNode)val.dptr;
1429     }
1430 
1431     return policy->root;
1432 }
1433 
1434 static OWPDPolicyNode
GetNodeFromUserID(OWPDPolicy policy,const OWPUserID userid)1435 GetNodeFromUserID(
1436         OWPDPolicy      policy,
1437         const OWPUserID userid  /* MUST BE VALID MEMORY */
1438         )
1439 {
1440     OWPDPidRec  pid;
1441     I2Datum     key,val;
1442 
1443     memset(&pid,0,sizeof(pid));
1444 
1445     pid.id_type = OWPDPidUserType;
1446     key.dptr = &pid;
1447     key.dsize = sizeof(pid);
1448 
1449     memcpy(pid.user.userid,userid,sizeof(pid.user.userid));
1450 
1451     if(I2HashFetch(policy->idents,key,&val)){
1452         return (OWPDPolicyNode)val.dptr;
1453     }
1454 
1455     return NULL;
1456 }
1457 
1458 static OWPDPolicyNode
GetNodeFromAddr(OWPDPolicy policy,struct sockaddr * remote_sa_addr)1459 GetNodeFromAddr(
1460         OWPDPolicy      policy,
1461         struct sockaddr *remote_sa_addr
1462         )
1463 {
1464     OWPDPidRec  pid;
1465     uint8_t    nbytes,nbits,*ptr;
1466     I2Datum     key,val;
1467 
1468     memset(&pid,0,sizeof(pid));
1469 
1470     pid.id_type = OWPDPidNetmaskType;
1471     key.dptr = &pid;
1472     key.dsize = sizeof(pid);
1473 
1474     switch(remote_sa_addr->sa_family){
1475         struct sockaddr_in        *saddr4;
1476 #ifdef        AF_INET6
1477         struct sockaddr_in6        *saddr6;
1478 
1479         case AF_INET6:
1480         saddr6 = (struct sockaddr_in6*)remote_sa_addr;
1481         /*
1482          * If this is a v4 mapped address - match it as a v4 address.
1483          */
1484         if(IN6_IS_ADDR_V4MAPPED(&saddr6->sin6_addr)){
1485             memcpy(pid.net.addrval,
1486                     &saddr6->sin6_addr.s6_addr[12],4);
1487             pid.net.addrsize = 4;
1488         }
1489         else{
1490             memcpy(pid.net.addrval,saddr6->sin6_addr.s6_addr,16);
1491             pid.net.addrsize = 16;
1492         }
1493         break;
1494 #endif
1495         case AF_INET:
1496         saddr4 = (struct sockaddr_in*)remote_sa_addr;
1497         memcpy(pid.net.addrval,&saddr4->sin_addr.s_addr,4);
1498         pid.net.addrsize = 4;
1499         break;
1500 
1501         default:
1502         OWPError(policy->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1503                 "Unknown address protocol family.");
1504         return NULL;
1505         break;
1506     }
1507 
1508     /*
1509      * Start with the max mask size (full address) and keep decreasing
1510      * the mask size until all possible address masks have been checked
1511      * for the given address.
1512      */
1513     for(pid.net.mask_len=pid.net.addrsize*8;
1514             pid.net.mask_len > 0; pid.net.mask_len--){
1515         /*
1516          * nbytes is number of complete bytes in "mask".
1517          * nbits is number of bits in the following byte that
1518          * are part of the "mask".
1519          */
1520         nbytes = pid.net.mask_len/8;
1521         nbits = pid.net.mask_len%8;
1522         ptr = &pid.net.addrval[nbytes];
1523 
1524         /*
1525          * Zero out one more bit each time through the loop.
1526          * (The "if" skips the "max" case.)
1527          */
1528         if(nbytes < pid.net.addrsize){
1529             *ptr &= (0xFF << (8-nbits));
1530         }
1531 
1532         if(I2HashFetch(policy->idents,key,&val)){
1533             return (OWPDPolicyNode)val.dptr;
1534         }
1535     }
1536 
1537     return GetNodeDefault(policy);
1538 }
1539 
1540 static OWPDLimitT
GetLimit(OWPDPolicyNode node,OWPDMesgT lim)1541 GetLimit(
1542         OWPDPolicyNode  node,
1543         OWPDMesgT       lim
1544         )
1545 {
1546     size_t  i;
1547 
1548     for(i=0;i<node->ilim;i++){
1549         if(lim == node->limits[i].limit){
1550             return node->limits[i].value;
1551         }
1552     }
1553 
1554     return GetDefLimit(lim);
1555 }
1556 
1557 static OWPDLimitT
GetUsed(OWPDPolicyNode node,OWPDMesgT lim)1558 GetUsed(
1559         OWPDPolicyNode  node,
1560         OWPDMesgT       lim
1561        )
1562 {
1563     size_t  i;
1564 
1565     for(i=0;i<node->ilim;i++){
1566         if(lim == node->limits[i].limit){
1567             return node->used[i].value;
1568         }
1569     }
1570 
1571     return 0;
1572 }
1573 
1574 /*
1575  * Returns True if the usage is less than the limit - false if it is greater.
1576  * It sets the usage to the value passed in either way. (well - only if the
1577  * resource is being tracked.)
1578  */
1579 static void
IntegerResourceUsage(OWPDPolicyNode node,OWPDLimRec lim)1580 IntegerResourceUsage(
1581         OWPDPolicyNode  node,
1582         OWPDLimRec      lim
1583         )
1584 {
1585     size_t  i;
1586 
1587     for(i=0;i<node->ilim;i++){
1588         if(node->limits[i].limit == lim.limit){
1589             goto found;
1590         }
1591     }
1592 
1593     /*
1594      * If there is not limit record, then the default must be 0 or the
1595      * logic breaks.
1596      */
1597     assert(!GetDefLimit(lim.limit));
1598 
1599     /*
1600      * No reason to keep track if this resource is unlimited all the
1601      * way up the tree - so just return true.
1602      */
1603     return;
1604 
1605 found:
1606     /*
1607      * Ok - found the resource limits
1608      */
1609 
1610     /*
1611      * If no limit at this level, just return true
1612      */
1613     if(!node->limits[i].value){
1614         return;
1615     }
1616 
1617     node->used[i].value = lim.value;
1618 
1619     if(node->used[i].value > node->limits[i].value){
1620         OWPError(node->policy->ctx,OWPErrWARNING,OWPErrPOLICY,
1621                 "Resource usage exceeds limits %s:%s "
1622                 "(used = %" PRIu64 ", limit = %" PRIu64 ")",node->nodename,
1623                 GetLimName(lim.limit),node->used[i].value,
1624                 node->limits[i].value);
1625     }
1626 
1627     return;
1628 }
1629 
1630 static void
OWPDResourceUsage(OWPDPolicyNode node,OWPDLimRec lim)1631 OWPDResourceUsage(
1632         OWPDPolicyNode  node,
1633         OWPDLimRec      lim
1634         )
1635 {
1636     size_t          maxdef = I2Number(limkeys);
1637     size_t          i;
1638     enum limtype    limkind = LIMNOT;
1639 
1640     for(i=0;i<maxdef;i++){
1641         if(lim.limit == limkeys[i].limit){
1642             limkind = limkeys[i].ltype;
1643             break;
1644         }
1645     }
1646 
1647     if(limkind != LIMINTVAL){
1648         return;
1649     }
1650 
1651     OWPError(node->policy->ctx,OWPErrDEBUG,OWPErrPOLICY,
1652             "ResInit %s:%s = %" PRIu64,node->nodename,
1653             GetLimName(lim.limit),lim.value);
1654     IntegerResourceUsage(node,lim);
1655 
1656     return;
1657 }
1658 
1659 static OWPBoolean
IntegerResourceDemand(OWPDPolicyNode node,OWPDMesgT query,OWPDLimRec lim)1660 IntegerResourceDemand(
1661         OWPDPolicyNode  node,
1662         OWPDMesgT       query,
1663         OWPDLimRec      lim
1664         )
1665 {
1666     size_t  i;
1667     double  fudge = 1.0;
1668 
1669     /*
1670      * terminate recursion
1671      */
1672     if(!node){
1673         return True;
1674     }
1675 
1676     for(i=0;i<node->ilim;i++){
1677         if(node->limits[i].limit == lim.limit){
1678             goto found;
1679         }
1680     }
1681 
1682     /*
1683      * If there is not limit record, then the default must be 0 or the
1684      * logic breaks.
1685      */
1686     assert(!GetDefLimit(lim.limit));
1687 
1688     /*
1689      * No reason to keep track if this resource is unlimited all the
1690      * way up the tree - so just return true.
1691      */
1692     return True;
1693 
1694 found:
1695     /*
1696      * Ok - found the resource limits
1697      */
1698 
1699     /*
1700      * If no limit at this level, go on to next.
1701      */
1702     if(!node->limits[i].value){
1703         return IntegerResourceDemand(node->parent,query,lim);
1704     }
1705 
1706     /*
1707      * Deal with resource releases.
1708      */
1709     else if(query == OWPDMESGRELEASE){
1710         if(lim.value > node->used[i].value){
1711             OWPError(node->policy->ctx,OWPErrFATAL,OWPErrPOLICY,
1712                     "Request to release unallocated resouces: "
1713                     "%s:%s (currently allocated = %u, "
1714                     "release amount = %u)",node->nodename,
1715                     GetLimName(lim.limit),node->used[i].value,
1716                     lim.value);
1717             return False;
1718         }
1719 
1720         if(!IntegerResourceDemand(node->parent,query,lim)){
1721             return False;
1722         }
1723 
1724         node->used[i].value -= lim.value;
1725 
1726         return True;
1727     }
1728 
1729     /*
1730      * The rest deals with resource requests.
1731      */
1732 
1733     /*
1734      * If this is a OWPDMESGCLAIM request - apply the fudge.
1735      */
1736     if(query == OWPDMESGCLAIM){
1737         switch(lim.limit){
1738             case OWPDLimDisk:
1739                 fudge = node->policy->diskfudge;
1740                 break;
1741 
1742             default:
1743                 OWPError(node->policy->ctx,OWPErrFATAL,OWPErrPOLICY,
1744                         "Invalid \"CLAIM\" request");
1745                 return False;
1746         }
1747     }
1748     else if(query != OWPDMESGREQUEST){
1749         OWPError(node->policy->ctx,OWPErrFATAL,OWPErrPOLICY,
1750                 "Unknown resource request type: %u",query);
1751         return False;
1752     }
1753 
1754     /*
1755      * If this level doesn't have the resources available - return false.
1756      */
1757     if((lim.value+node->used[i].value) > (node->limits[i].value * fudge)){
1758         return False;
1759     }
1760 
1761     /*
1762      * Are the resource available the next level up?
1763      */
1764     if(!IntegerResourceDemand(node->parent,query,lim)){
1765         return False;
1766     }
1767 
1768     node->used[i].value += lim.value;
1769 
1770     return True;
1771 }
1772 
1773 OWPBoolean
OWPDResourceDemand(OWPDPolicyNode node,OWPDMesgT query,OWPDLimRec lim)1774 OWPDResourceDemand(
1775         OWPDPolicyNode  node,
1776         OWPDMesgT       query,
1777         OWPDLimRec      lim
1778         )
1779 {
1780     size_t          maxdef = I2Number(limkeys);
1781     size_t          i;
1782     enum limtype    limkind = LIMNOT;
1783     OWPDLimitT      val;
1784     OWPBoolean      ret;
1785 
1786     for(i=0;i<maxdef;i++){
1787         if(lim.limit == limkeys[i].limit){
1788             limkind = limkeys[i].ltype;
1789             break;
1790         }
1791     }
1792 
1793     if(limkind == LIMNOT){
1794         return False;
1795     }
1796 
1797     if(limkind == LIMBOOLVAL){
1798         if(query == OWPDMESGRELEASE){
1799             return True;
1800         }
1801         val = GetLimit(node,lim.limit);
1802         return (val == lim.value);
1803     }
1804 
1805     ret = IntegerResourceDemand(node,query,lim);
1806 
1807     /*
1808      * These messages are printed to INFO so they can be selected
1809      * as non-interesting.
1810      */
1811     OWPError(node->policy->ctx,OWPErrDEBUG,OWPErrPOLICY,
1812             "ResReq %s: %s:%s:%s = %" PRIu64 " (result = %" PRIu64
1813             ", limit = %" PRIu64 ")",
1814             (ret)?"ALLOWED":"DENIED",
1815             node->nodename,
1816             (query == OWPDMESGRELEASE)?"release":"request",
1817             GetLimName(lim.limit),
1818             lim.value,
1819             GetUsed(node,lim.limit),
1820             GetLimit(node,lim.limit));
1821     for(node = node->parent;!ret && node;node = node->parent){
1822         OWPError(node->policy->ctx,OWPErrDEBUG,OWPErrPOLICY,
1823                 "ResReq %s: %s:%s:%s = %" PRIu64
1824                 " (result = %" PRIu64 ", limit = %" PRIu64")",
1825                 (ret)?"ALLOWED":"DENIED",
1826                 node->nodename,
1827                 (query == OWPDMESGRELEASE)?"release":"request",
1828                 GetLimName(lim.limit),
1829                 lim.value,
1830                 GetUsed(node,lim.limit),
1831                 GetLimit(node,lim.limit));
1832     }
1833 
1834     return ret;
1835 }
1836 
1837 /*
1838  * Function:        OWPDSendResponse
1839  *
1840  * Description:
1841  *         This function is called from the parent perspective.
1842  *
1843  *         It is used to respond to a child request/release of resources.
1844  *
1845  * In Args:
1846  *
1847  * Out Args:
1848  *
1849  * Scope:
1850  * Returns:
1851  * Side Effect:
1852  */
1853 int
OWPDSendResponse(int fd,OWPDMesgT mesg)1854 OWPDSendResponse(
1855         int         fd,
1856         OWPDMesgT   mesg
1857         )
1858 {
1859     OWPDMesgT   buf[3];
1860     int         fail_on_intr=1;
1861 
1862     buf[0] = buf[2] = OWPDMESGMARK;
1863     buf[1] = mesg;
1864 
1865     if(I2Writeni(fd,&buf[0],12,&fail_on_intr) != 12){
1866         return 1;
1867     }
1868 
1869     return 0;
1870 }
1871 
1872 /*
1873  * Function:        OWPDReadResponse
1874  *
1875  * Description:
1876  *
1877  * In Args:
1878  *
1879  * Out Args:
1880  *
1881  * Scope:
1882  * Returns:
1883  * Side Effect:
1884  */
1885 static OWPDMesgT
OWPDReadResponse(int fd)1886 OWPDReadResponse(
1887         int fd
1888         )
1889 {
1890     OWPDMesgT   buf[3];
1891     int         fail_on_intr=1;
1892 
1893     if(I2Readni(fd,&buf[0],12,&fail_on_intr) != 12){
1894         return OWPDMESGINVALID;
1895     }
1896 
1897     if((buf[0] != OWPDMESGMARK) || (buf[2] != OWPDMESGMARK)){
1898         return OWPDMESGINVALID;
1899     }
1900 
1901     return buf[1];
1902 }
1903 
1904 /*
1905  * Function:        OWPDReadClass
1906  *
1907  * Description:
1908  *         This function is called from the parent perspective.
1909  *
1910  *         It is used to read the initial message from a child to determine
1911  *         the "user class" of the given connection.
1912  *
1913  *
1914  * In Args:
1915  *
1916  * Out Args:
1917  *
1918  * Scope:
1919  * Returns:
1920  * Side Effect:
1921  */
1922 OWPDPolicyNode
OWPDReadClass(OWPDPolicy policy,int fd,int * err)1923 OWPDReadClass(
1924         OWPDPolicy  policy,
1925         int         fd,
1926         int         *err
1927         )
1928 {
1929     ssize_t         i;
1930     const OWPDMesgT mark=OWPDMESGMARK;
1931     const OWPDMesgT mclass=OWPDMESGCLASS;
1932     uint8_t        buf[OWPDMAXCLASSLEN+1 + sizeof(OWPDMesgT)*3];
1933     I2Datum         key,val;
1934     int             fail_on_intr=1;
1935 
1936     *err = 1;
1937 
1938     /*
1939      * Read message header
1940      */
1941     if((i = I2Readni(fd,&buf[0],8,&fail_on_intr)) != 8){
1942         if(i == 0){
1943             *err = 0;
1944         }
1945         return NULL;
1946     }
1947 
1948     if(memcmp(&buf[0],&mark,sizeof(OWPDMesgT)) ||
1949             memcmp(&buf[4],&mclass,sizeof(OWPDMesgT))){
1950         return NULL;
1951     }
1952 
1953     /*
1954      * read classname
1955      */
1956     for(i=0;i<= OWPDMAXCLASSLEN;i++){
1957         if(I2Readni(fd,&buf[i],1,&fail_on_intr) != 1){
1958             return NULL;
1959         }
1960 
1961         if(buf[i] == '\0'){
1962             break;
1963         }
1964     }
1965 
1966     if(i > OWPDMAXCLASSLEN){
1967         return NULL;
1968     }
1969 
1970     key.dptr = &buf[0];
1971     key.dsize = i;
1972 
1973     /*
1974      * read message trailer.
1975      */
1976     i++;
1977     if((I2Readni(fd,&buf[i],4,&fail_on_intr) != 4) ||
1978             memcmp(&buf[i],&mark,sizeof(OWPDMesgT))){
1979         return NULL;
1980     }
1981 
1982     if(I2HashFetch(policy->limits,key,&val)){
1983         if(OWPDSendResponse(fd,OWPDMESGOK) != 0){
1984             return NULL;
1985         }
1986         *err = 0;
1987         return val.dptr;
1988     }
1989 
1990     (void)OWPDSendResponse(fd,OWPDMESGDENIED);
1991     return NULL;
1992 }
1993 
1994 static OWPDMesgT
OWPDSendClass(OWPDPolicy policy,OWPDPolicyNode node)1995 OWPDSendClass(
1996         OWPDPolicy      policy,
1997         OWPDPolicyNode  node
1998         )
1999 {
2000     uint8_t    buf[OWPDMAXCLASSLEN+1 + sizeof(OWPDMesgT)*3];
2001     OWPDMesgT   mesg;
2002     ssize_t     len;
2003     int         fail_on_intr=1;
2004 
2005     mesg = OWPDMESGMARK;
2006     memcpy(&buf[0],&mesg,4);
2007     mesg = OWPDMESGCLASS;
2008     memcpy(&buf[4],&mesg,4);
2009     len = strlen(node->nodename);
2010     len++;
2011     strncpy((char*)&buf[8],node->nodename,len);
2012     len += 8;
2013     mesg = OWPDMESGMARK;
2014     memcpy(&buf[len],&mesg,4);
2015     len += 4;
2016 
2017     if(I2Writeni(policy->fd,buf,len,&fail_on_intr) != len){
2018         OWPError(policy->ctx,OWPErrFATAL,OWPErrUNKNOWN,
2019                 "OWPDCheckControlPolicy: Unable to contact parent");
2020         return OWPDMESGINVALID;
2021     }
2022 
2023     return OWPDReadResponse(policy->fd);
2024 }
2025 
2026 /*
2027  * True if there is a request
2028  */
2029 OWPBoolean
OWPDReadQuery(int fd,OWPDMesgT * query,OWPDLimRec * lim_ret,int * err)2030 OWPDReadQuery(
2031         int         fd,
2032         OWPDMesgT   *query,
2033         OWPDLimRec  *lim_ret,
2034         int         *err
2035         )
2036 {
2037     ssize_t     i;
2038     OWPDMesgT   buf[7];
2039     int         fail_on_intr=1;
2040 
2041     *err = 1;
2042 
2043     /*
2044      * Read message header
2045      */
2046     if((i = I2Readni(fd,&buf[0],28,&fail_on_intr)) != 28){
2047         if(i == 0){
2048             *err = 0;
2049         }
2050         return False;
2051     }
2052 
2053     if((buf[0] != OWPDMESGMARK) || (buf[6] != OWPDMESGMARK) ||
2054             (buf[1] != OWPDMESGRESOURCE)){
2055         return False;
2056     }
2057 
2058     switch(buf[2]){
2059         case OWPDMESGREQUEST:
2060         case OWPDMESGRELEASE:
2061         case OWPDMESGCLAIM:
2062             *query = buf[2];
2063             break;
2064         default:
2065             return False;
2066     }
2067 
2068     lim_ret->limit = buf[3];
2069     memcpy(&lim_ret->value,&buf[4],8);
2070 
2071     *err = 0;
2072 
2073     return True;
2074 }
2075 
2076 static OWPDMesgT
OWPDQuery(OWPDPolicy policy,OWPDMesgT mesg,OWPDLimRec lim)2077 OWPDQuery(
2078         OWPDPolicy  policy,
2079         OWPDMesgT   mesg,   /* OWPDMESGREQUEST or OWPDMESGRELEASE   */
2080         OWPDLimRec  lim
2081         )
2082 {
2083     OWPDMesgT   buf[7];
2084     int         fail_on_intr=1;
2085 
2086     buf[0] = buf[6] = OWPDMESGMARK;
2087     buf[1] = OWPDMESGRESOURCE;
2088     buf[2] = mesg;
2089     buf[3] = lim.limit;
2090     memcpy(&buf[4],&lim.value,8);
2091 
2092     if(I2Writeni(policy->fd,buf,28,&fail_on_intr) != 28){
2093         OWPError(policy->ctx,OWPErrFATAL,OWPErrUNKNOWN,
2094                 "OWPDQuery: Unable to contact parent");
2095         return OWPDMESGINVALID;
2096     }
2097 
2098     return OWPDReadResponse(policy->fd);
2099 }
2100 
2101 /*
2102  * Function:        OWPDAllowOpenMode
2103  *
2104  * Description:
2105  *        check if the given address is allowed to have open_mode communication.
2106  *
2107  * In Args:
2108  *
2109  * Out Args:
2110  *
2111  * Scope:
2112  * Returns:
2113  * Side Effect:
2114  */
2115 OWPBoolean
OWPDAllowOpenMode(OWPDPolicy policy,struct sockaddr * remote_sa_addr,OWPErrSeverity * err_ret)2116 OWPDAllowOpenMode(
2117         OWPDPolicy      policy,
2118         struct sockaddr *remote_sa_addr,
2119         OWPErrSeverity  *err_ret            /* error - return   */
2120         )
2121 {
2122     OWPDPolicyNode  node;
2123 
2124     *err_ret = OWPErrOK;
2125 
2126     if(!(node = GetNodeFromAddr(policy,remote_sa_addr))){
2127         OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
2128                 "OWPDAllowOpenMode: Invalid policy");
2129         *err_ret = OWPErrFATAL;
2130         return False;
2131     }
2132 
2133     return GetLimit(node,OWPDLimAllowOpenMode);
2134 }
2135 
2136 /*
2137  * Function:        OWPDCheckControlPolicy
2138  *
2139  * Description:
2140  *         Determines the "user class" of the given connection and
2141  *         sends that information to the "parent" so the parent can
2142  *         approve future resource requests.
2143  *
2144  *         Returns False and sets err_ret if the "user class" cannot be
2145  *         determined or if there is an error communicating with the parent.
2146  *         (The parent communication is necessary to keep track of resource
2147  *         allocations on a "global" basis instead of per-connection.)
2148  *
2149  * In Args:
2150  *
2151  * Out Args:
2152  *
2153  * Scope:
2154  * Returns:
2155  * Side Effect:
2156  */
2157 OWPBoolean
OWPDCheckControlPolicy(OWPControl cntrl,OWPSessionMode mode,const OWPUserID userid,struct sockaddr * local_sa_addr,struct sockaddr * remote_sa_addr,OWPErrSeverity * err_ret)2158 OWPDCheckControlPolicy(
2159         OWPControl      cntrl,
2160         OWPSessionMode  mode,               /* requested mode       */
2161         const OWPUserID userid,             /* identity             */
2162         struct sockaddr *local_sa_addr      __attribute__((unused)),
2163                                             /* local addr or NULL   */
2164         struct sockaddr *remote_sa_addr,    /* remote addr          */
2165         OWPErrSeverity  *err_ret            /* error - return       */
2166         )
2167 {
2168     OWPContext      ctx;
2169     OWPDPolicy      policy;
2170     OWPDPolicyNode  node=NULL;
2171     I2Datum         key,val;
2172     OWPDMesgT       ret;
2173 
2174     *err_ret = OWPErrOK;
2175 
2176     ctx = OWPGetContext(cntrl);
2177 
2178     if(!(policy = (OWPDPolicy)OWPContextConfigGetV(ctx,OWPDPOLICY))){
2179         OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
2180                 "OWPDCheckControlPolicy: OWPDPOLICY not set");
2181         *err_ret = OWPErrFATAL;
2182         return False;
2183     }
2184 
2185     /*
2186      * Determine userclass and send that to the parent.
2187      * (First try based on userid.)
2188      */
2189     if(((mode & OWP_MODE_DOCIPHER) && userid) &&
2190             !(node = GetNodeFromUserID(policy,userid))){
2191         OWPError(policy->ctx,OWPErrDEBUG,OWPErrUNKNOWN,
2192                 "OWPDCheckControlPolicy: No policy match for userid(%s) - using netmask match",userid);
2193     }
2194 
2195     if((mode & OWP_MODE_DOCIPHER) && userid){
2196         key.dptr = (void*)userid;
2197         key.dsize = strlen(userid);
2198 
2199         if(I2HashFetch(policy->limits,key,&val)){
2200             node = val.dptr;
2201         }
2202     }
2203 
2204     /*
2205      * If we don't have a userclass from the userid, then get one
2206      * based on the address. (This returns the default if no
2207      * address matched.)
2208      */
2209     if(!node && !(node = GetNodeFromAddr(policy,remote_sa_addr))){
2210         OWPError(policy->ctx,OWPErrFATAL,OWPErrINVALID,
2211                 "OWPDCheckControlPolicy: Invalid policy");
2212         *err_ret = OWPErrFATAL;
2213         return False;
2214     }
2215 
2216     /*
2217      * Initialize the communication with the parent resource broker
2218      * process.
2219      */
2220     if((ret = OWPDSendClass(policy,node)) == OWPDMESGOK){
2221         /*
2222          * Success - now save the node in the control config
2223          * for later hook functions to access.
2224          */
2225         if(!OWPControlConfigSetV(cntrl,OWPDPOLICY_NODE,node)){
2226             OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2227                     "OWPDCheckControlPolicy: Unable to save \"class\" for connection");
2228             *err_ret = OWPErrFATAL;
2229             return False;
2230         }
2231 
2232         return True;
2233     }
2234 
2235     /*
2236      * If ret wasn't OWPDMESGDENIED - there was some kind of error.
2237      */
2238     if(ret != OWPDMESGDENIED){
2239         *err_ret = OWPErrFATAL;
2240     }
2241 
2242     return False;
2243 }
2244 
2245 /*
2246  * This structure is used to keep track of the path information used by
2247  * a fp allocated by the OWPDOpenFile function.
2248  * This macro is the prefix for a given finfo in the cntrl Config table. The
2249  * fd number is concatenated to this string (in ascii) to get a key for
2250  * adding and removing a finfo record to the Config table.
2251  */
2252 #define OWPDPOLICY_KEYLEN   64
2253 #define OWPDPOLICY_FILEINFO "OWPDPOLICY_FILEINFO"
2254 typedef struct OWPDFileInformationRec{
2255     OWPDPolicyNode  node;   /* node specific to file, not connection */
2256     FILE            *fp;
2257     char            filepath[PATH_MAX+1];
2258     char            linkpath[PATH_MAX+1];
2259 } OWPDFileInformationRec, *OWPDFileInformation;
2260 
2261 /*
2262  * Enum used to keep track of the 'type' of the structure union
2263  */
2264 typedef enum {OWPDINFO_INVALID=0,OWPDINFO_FETCH,OWPDINFO_TEST} OWPDInfoType;
2265 
2266 /*
2267  * This structure is returned in the "closure" pointer of the CheckTestPolicy
2268  * pointer - and provided to the Open/Close file functions as well as the
2269  * TestComplete function.
2270  */
2271 typedef struct OWPDInfoTestRec{
2272     OWPDInfoType        itype;
2273     OWPDPolicyNode      node;
2274     OWPDFileInformation finfo;
2275     OWPDLimRec          res[2];        /* 0=bandwidth,1=disk */
2276 } OWPDInfoTestRec, *OWPDInfoTest;
2277 
2278 typedef struct OWPDInfoFetchRec{
2279     OWPDInfoType        itype;
2280     OWPDPolicyNode      node;
2281     OWPDFileInformation finfo;
2282     uint32_t            begin;
2283     uint32_t            end;
2284 } OWPDInfoFetchRec, *OWPDInfoFetch;
2285 
2286 union OWPDInfoRequestUnion{
2287     OWPDInfoType        itype;
2288     OWPDInfoTestRec     test;
2289     OWPDInfoFetchRec    fetch;
2290 };
2291 
2292 typedef union OWPDInfoRequestUnion OWPDInfoRequestRec, *OWPDInfoRequest;
2293 
2294 OWPBoolean
OWPDCheckTestPolicy(OWPControl cntrl,OWPBoolean local_sender,struct sockaddr * local_sa_addr,struct sockaddr * remote_sa_addr,socklen_t sa_len,OWPTestSpec * test_spec,void ** closure,OWPErrSeverity * err_ret)2295 OWPDCheckTestPolicy(
2296         OWPControl      cntrl,
2297         OWPBoolean      local_sender,
2298         struct sockaddr *local_sa_addr      __attribute__((unused)),
2299         struct sockaddr *remote_sa_addr,
2300         socklen_t       sa_len              __attribute__((unused)),
2301         OWPTestSpec     *test_spec,
2302         void            **closure,
2303         OWPErrSeverity  *err_ret
2304         )
2305 {
2306     OWPContext      ctx = OWPGetContext(cntrl);
2307     OWPDPolicyNode  node;
2308     OWPDInfoTest    tinfo;
2309     OWPDMesgT       ret;
2310 
2311     *err_ret = OWPErrOK;
2312 
2313     /*
2314      * Fetch the "user class" for this connection.
2315      */
2316     if(!(node = (OWPDPolicyNode)OWPControlConfigGetV(cntrl,
2317                     OWPDPOLICY_NODE))){
2318         OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
2319                 "OWPDCheckTestPolicy: OWPDPOLICY_NODE not set");
2320         *err_ret = OWPErrFATAL;
2321         return False;
2322     }
2323 
2324 
2325     if(!(tinfo = calloc(1,sizeof(OWPDInfoTestRec)))){
2326         OWPError(ctx,OWPErrFATAL,errno,"calloc(1,OWPDInfoTestRec): %M");
2327         *err_ret = OWPErrFATAL;
2328         return False;
2329     }
2330 
2331     tinfo->itype = OWPDINFO_TEST;
2332     tinfo->node = node;
2333 
2334     /*
2335      * Check bandwidth
2336      */
2337     tinfo->res[0].limit = OWPDLimBandwidth;
2338     tinfo->res[0].value = OWPTestPacketBandwidth(ctx,
2339             remote_sa_addr->sa_family,OWPGetMode(cntrl),test_spec);
2340     if((ret = OWPDQuery(node->policy,OWPDMESGREQUEST,tinfo->res[0]))
2341             == OWPDMESGDENIED){
2342         goto done;
2343     }
2344     if(ret == OWPDMESGINVALID){
2345         *err_ret = OWPErrFATAL;
2346         goto done;
2347     }
2348 
2349 
2350     /*
2351      * If we are receiver - check disk-space.
2352      */
2353     if(!local_sender){
2354         /*
2355          * Request 10% more than our estimate to cover duplicates.
2356          * reality will be adjusted in CloseFile.
2357          */
2358         tinfo->res[1].limit = OWPDLimDisk;
2359         tinfo->res[1].value = OWPTestDiskspace(test_spec);
2360 
2361         if((ret = OWPDQuery(node->policy,OWPDMESGREQUEST,tinfo->res[1]))
2362                 == OWPDMESGDENIED){
2363             OWPDQuery(node->policy,OWPDMESGRELEASE,tinfo->res[0]);
2364             goto done;
2365         }
2366         if(ret == OWPDMESGINVALID){
2367             *err_ret = OWPErrFATAL;
2368             goto done;
2369         }
2370     }
2371 
2372     *closure = tinfo;
2373     return True;
2374 done:
2375     free(tinfo);
2376     return False;
2377 }
2378 
2379 OWPBoolean
OWPDCheckFetchPolicy(OWPControl cntrl,struct sockaddr * local_sa_addr,struct sockaddr * remote_sa_addr,socklen_t sa_len,uint32_t begin,uint32_t end,OWPSID sid,void ** closure,OWPErrSeverity * err_ret)2380 OWPDCheckFetchPolicy(
2381         OWPControl      cntrl,
2382         struct sockaddr *local_sa_addr      __attribute__((unused)),
2383         struct sockaddr *remote_sa_addr     __attribute__((unused)),
2384         socklen_t       sa_len              __attribute__((unused)),
2385         uint32_t        begin,
2386         uint32_t        end,
2387         OWPSID          sid                 __attribute__((unused)),
2388         void            **closure,
2389         OWPErrSeverity  *err_ret
2390         )
2391 {
2392     OWPContext      ctx = OWPGetContext(cntrl);
2393     OWPDPolicyNode  node;
2394     OWPDInfoFetch   fetch;
2395 
2396     *err_ret = OWPErrOK;
2397 
2398     /*
2399      * Fetch the "user class" for this connection.
2400      */
2401     if(!(node = (OWPDPolicyNode)OWPControlConfigGetV(cntrl,
2402                     OWPDPOLICY_NODE))){
2403         OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
2404                 "OWPDCheckTestPolicy: OWPDPOLICY_NODE not set");
2405         *err_ret = OWPErrFATAL;
2406         return False;
2407     }
2408 
2409     /*
2410      * Could implement something here that only allowed the user-class
2411      * that created the data to fetch it, but for now this function
2412      * is only used to keep track of what was actually requested so
2413      * the CloseFile function can properly implement delete_on_fetch
2414      * functionality. (Only delete the file if the entire file is
2415      * requested.)
2416      */
2417     if(!(fetch = calloc(1,sizeof(OWPDInfoFetchRec)))){
2418         OWPError(ctx,OWPErrFATAL,errno,"calloc(1,OWPDInfoFetchRec): %M");
2419         *err_ret = OWPErrFATAL;
2420         return False;
2421     }
2422 
2423     fetch->itype = OWPDINFO_FETCH;
2424     fetch->node = node;
2425     fetch->begin = begin;
2426     fetch->end = end;
2427 
2428     *closure = fetch;
2429     return True;
2430 }
2431 
2432 extern void
OWPDTestComplete(OWPControl cntrl,void * closure,OWPAcceptType aval)2433 OWPDTestComplete(
2434         OWPControl      cntrl       __attribute__((unused)),
2435         void            *closure,   /* closure from CheckTestPolicy        */
2436         OWPAcceptType   aval        __attribute__((unused))
2437         )
2438 {
2439     OWPDInfoRequest rinfo = (OWPDInfoRequest)closure;
2440     OWPDInfoTest    tinfo = NULL;
2441     int             i;
2442 
2443     if(!rinfo || (rinfo->itype != OWPDINFO_TEST)){
2444         OWPError(OWPGetContext(cntrl),OWPErrFATAL,OWPErrINVALID,
2445                 "OWPDTestComplete: Invalid closure");
2446         return;
2447     }
2448 
2449     tinfo = &rinfo->test;
2450 
2451     for(i=0;i<2;i++){
2452         if(!tinfo->res[i].limit){
2453             continue;
2454         }
2455         (void)OWPDQuery(tinfo->node->policy,OWPDMESGRELEASE,
2456                         tinfo->res[i]);
2457     }
2458 
2459     if(tinfo->finfo){
2460         OWPError(OWPGetContext(cntrl),OWPErrWARNING,OWPErrUNKNOWN,
2461                 "OWPDTestComplete: finfo not closed?");
2462     }
2463 
2464     free(tinfo);
2465 
2466     return;
2467 }
2468 
2469 /*
2470  * Function:        OWPDOpenFile
2471  *
2472  * Description:
2473  *         This function opens a file and saves state about it in the
2474  *         cntrl State hash. This is used to implement policy.
2475  *
2476  * In Args:
2477  *
2478  * Out Args:
2479  *
2480  * Scope:
2481  * Returns:
2482  * Side Effect:
2483  */
2484 FILE*
OWPDOpenFile(OWPControl cntrl,void * closure,OWPSID sid,char fname_ret[PATH_MAX+1])2485 OWPDOpenFile(
2486         OWPControl  cntrl,
2487         void        *closure,
2488         OWPSID      sid,
2489         char        fname_ret[PATH_MAX+1]
2490         )
2491 {
2492     OWPContext          ctx = OWPGetContext(cntrl);
2493     OWPDInfoRequest     rinfo = (OWPDInfoRequest)closure;
2494     OWPDInfoTest        tinfo = NULL;
2495     OWPDInfoFetch       xinfo;
2496     OWPDFileInformation finfo;
2497     OWPDPolicyNode      node;
2498     char                sid_name[sizeof(OWPSID)*2+1];
2499 
2500     if(!rinfo || (rinfo->itype == OWPDINFO_INVALID)){
2501         OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
2502                 "OWPDOpenFile: closure not set");
2503         return NULL;
2504     }
2505 
2506     /*
2507      * Hex Encode the sid.
2508      */
2509     I2HexEncode(sid_name,sid,sizeof(OWPSID));
2510 
2511     if(!(finfo = (calloc(1,sizeof(*finfo))))){
2512         OWPError(ctx,OWPErrFATAL,errno,"calloc(OWPDFileInformation): %M");
2513         return NULL;
2514     }
2515 
2516     if(rinfo->itype == OWPDINFO_TEST){
2517         tinfo = &rinfo->test;
2518 
2519         node = tinfo->node;
2520 
2521         /*
2522          * Now place pathname to catalog dir in finfo->linkpath.
2523          */
2524         strcpy(finfo->linkpath,node->policy->datadir);
2525         strcat(finfo->linkpath,OWP_PATH_SEPARATOR);
2526         strcat(finfo->linkpath,OWP_CATALOG_DIR);
2527 
2528         finfo->node = tinfo->node;
2529 
2530         /*
2531          * Make sure the node directory exists first.
2532          * (setting add_chars to the length of the filename part
2533          * that needs to be concatenated on after the directory.
2534          * This does not include the nul byte.)
2535          */
2536         if(!node_dir(ctx,True,node->policy->datadir,node,
2537                     OWP_PATH_SEPARATOR_LEN + (sizeof(OWPSID)*2) +
2538                     strlen(OWP_FILE_EXT),finfo->filepath)){
2539             return NULL;
2540         }
2541 
2542         strcat(finfo->filepath,OWP_PATH_SEPARATOR);
2543         strcat(finfo->filepath,sid_name);
2544         strcat(finfo->filepath,OWP_FILE_EXT);
2545 
2546         /*
2547          * we know top-level datadir exists from last call to
2548          * node_dir, now make sure "catalog" directory exists.
2549          */
2550         if((mkdir(finfo->linkpath,0755) != 0) && (errno != EEXIST)){
2551             OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"Unable to mkdir(%s): %M",
2552                     finfo->linkpath);
2553             return NULL;
2554         }
2555 
2556         strcat(finfo->linkpath,OWP_PATH_SEPARATOR);
2557         strcat(finfo->linkpath,sid_name);
2558         strcat(finfo->linkpath,OWP_FILE_EXT);
2559 
2560         /*
2561          * Now open the file.
2562          */
2563         if(!(finfo->fp = fopen(finfo->filepath,"w+b"))){
2564             OWPError(ctx,OWPErrFATAL,errno,"fopen(%s,\"wb\"): %M",
2565                     finfo->filepath);
2566             goto error;
2567         }
2568 
2569         /*
2570          * Create the symlink first.
2571          * This is how fetchsession will find the file.
2572          */
2573         if(symlink(finfo->filepath,finfo->linkpath) != 0){
2574             OWPError(ctx,OWPErrFATAL,errno,"symlink(%s,%s): %M",
2575                     finfo->filepath,finfo->linkpath);
2576             goto error;
2577         }
2578 
2579         if(fname_ret){
2580             strcpy(fname_ret,finfo->filepath);
2581         }
2582 
2583         /*
2584          * finfo is retrieved via closure for receive sessions.
2585          */
2586         tinfo->finfo = finfo;
2587     }
2588     else if(rinfo->itype == OWPDINFO_FETCH){
2589         int     len1,len2;
2590         char    tc;
2591         char    *dname;
2592         I2Datum key,val;
2593 
2594         xinfo = &rinfo->fetch;
2595 
2596         node = xinfo->node;
2597 
2598         /*
2599          * Now place pathname to catalog dir in finfo->linkpath.
2600          */
2601         strcpy(finfo->linkpath,node->policy->datadir);
2602         strcat(finfo->linkpath,OWP_PATH_SEPARATOR);
2603         strcat(finfo->linkpath,OWP_CATALOG_DIR);
2604         strcat(finfo->linkpath,OWP_PATH_SEPARATOR);
2605         strcat(finfo->linkpath,sid_name);
2606         strcat(finfo->linkpath,OWP_FILE_EXT);
2607         finfo->filepath[0] = '\0';
2608 
2609         /*
2610          * Determine the "real" filename so it
2611          * can be used to find the "policy" node for this file.
2612          * Policy for this file (delete_on_fetch) is determined by
2613          * the "user class" that created the file, not the
2614          * "user class" of the current fetch session.
2615          */
2616 
2617         /*
2618          * set len1 to length of path "after" last node.
2619          */
2620         len1 = OWP_PATH_SEPARATOR_LEN + sizeof(OWPSID)*2 +
2621             strlen(OWP_FILE_EXT);
2622 
2623         /*
2624          * len2 becomes length of the full filepath
2625          */
2626         if((len2 = readlink(finfo->linkpath,finfo->filepath,
2627                         PATH_MAX)) < 1){
2628             OWPError(ctx,OWPErrFATAL,errno,
2629                     "readlink(%s): %M",finfo->linkpath);
2630             goto error;
2631         }
2632         /*
2633          * If the filepath is longer than PATH_MAX or shorter than
2634          * len1 - it can't be valid.
2635          */
2636         if(((size_t)len2 >= sizeof(finfo->filepath)) || (len2 < len1)){
2637             OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
2638                     "readlink(%s): Invalid link",
2639                     finfo->linkpath);
2640             goto error;
2641         }
2642 
2643         /*
2644          * terminate full filepath
2645          */
2646         finfo->filepath[len2] = '\0';
2647 
2648         /*
2649          * temporarily terminate filepath just after last nodename
2650          * to fetch dirname component. (Not using libgen "dirname"
2651          * because libgen doesn't look to exist everywhere...)
2652          *
2653          * strrchr/rindex would probably be nicer than what I'm doing
2654          * here...
2655          */
2656         tc = finfo->filepath[len2-len1];
2657         finfo->filepath[len2-len1] = '\0';
2658         dname = &finfo->filepath[len2-len1] - 1;
2659         while(dname > finfo->filepath){
2660             if(!strncmp(dname,OWP_PATH_SEPARATOR,
2661                         OWP_PATH_SEPARATOR_LEN)){
2662                 dname += OWP_PATH_SEPARATOR_LEN;
2663                 break;
2664             }
2665             dname--;
2666         }
2667         if(dname <= finfo->filepath){
2668             OWPError(node->policy->ctx,OWPErrFATAL,OWPErrPOLICY,
2669                     "Unable to determine policy for %s",
2670                     finfo->linkpath);
2671             goto error;
2672         }
2673 
2674         /*
2675          * Now that we have a dirname - try fetching the policy
2676          * node for it.
2677          */
2678         key.dptr = dname;
2679         key.dsize = &finfo->filepath[len2-len1] - dname;
2680         if(!I2HashFetch(node->policy->limits,key,&val)){
2681             OWPError(node->policy->ctx,OWPErrFATAL,OWPErrPOLICY,
2682                     "Unable to determine policy for %s: class %s",
2683                     finfo->linkpath,dname);
2684         }
2685 
2686         /*
2687          * assign the node.
2688          */
2689         finfo->node = val.dptr;
2690 
2691         /*
2692          * reset the char pulled from the filepath to terminate
2693          * the nodename.
2694          */
2695         finfo->filepath[len2-len1] = tc;
2696 
2697         /*
2698          * Now open the file.
2699          */
2700         if(!(finfo->fp = fopen(finfo->linkpath,"rb"))){
2701             OWPError(ctx,OWPErrFATAL,errno,"fopen(%s,\"rb\"): %M",
2702                     finfo->linkpath);
2703             goto error;
2704         }
2705         if(fname_ret){
2706             strcpy(fname_ret,finfo->linkpath);
2707         }
2708 
2709         xinfo->finfo = finfo;
2710     }
2711     else{
2712         OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
2713                 "OWPDOpenFile: invalid closure");
2714         goto error;
2715     }
2716 
2717     return finfo->fp;
2718 
2719 error:
2720     if(tinfo){
2721         (void)unlink(finfo->linkpath);
2722         (void)unlink(finfo->filepath);
2723     }
2724 
2725     if(finfo->fp){
2726         fclose(finfo->fp);
2727     }
2728 
2729     free(finfo);
2730 
2731     return NULL;
2732 }
2733 
2734 /*
2735  * Function:        OWPDCloseFile
2736  *
2737  * Description:
2738  *         This function closes a file and looks at the cntrl state hash to
2739  *         determine if additional action should be performed to adhere to
2740  *         policy. i.e. should the file be unlinked now that it has been
2741  *         read? (del_on_fetch)
2742  *
2743  * In Args:
2744  *
2745  * Out Args:
2746  *
2747  * Scope:
2748  * Returns:
2749  * Side Effect:
2750  */
2751 extern void
OWPDCloseFile(OWPControl cntrl,void * closure,FILE * fp,OWPAcceptType aval)2752 OWPDCloseFile(
2753         OWPControl      cntrl,
2754         void            *closure,
2755         FILE            *fp,
2756         OWPAcceptType   aval
2757         )
2758 {
2759     OWPDInfoRequest     rinfo = (OWPDInfoRequest)closure;
2760     OWPDInfoTest        tinfo = NULL;
2761     OWPDInfoFetch       xinfo = NULL;
2762     OWPDFileInformation finfo = NULL;
2763     OWPContext          ctx = OWPGetContext(cntrl);
2764     struct stat         sbuf;
2765     OWPDMesgT           mesg,ret;
2766     OWPDLimRec          lim;
2767 
2768     if(!rinfo || (rinfo->itype == OWPDINFO_INVALID)){
2769         OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
2770                 "OWPDCloseFile: closure not set");
2771         return;
2772     }
2773 
2774     /*
2775      * File was from a TestRequest
2776      */
2777     if(rinfo->itype == OWPDINFO_TEST){
2778         /*
2779          * This was a receive endpoint. revise resource
2780          * request to reality.
2781          */
2782         tinfo = &rinfo->test;
2783         finfo = tinfo->finfo;
2784         tinfo->finfo = NULL;
2785 
2786 
2787         /*
2788          * stat the file to determine how much disk was actually
2789          * used.
2790          */
2791         if(fstat(fileno(fp),&sbuf) != 0){
2792             OWPError(ctx,OWPErrFATAL,errno,
2793                     "OWPDCloseFile: fstat(): %M: Unable to determine filesize...");
2794             goto end;
2795         }
2796 
2797         assert(tinfo->res[1].limit == OWPDLimDisk);
2798         lim.limit = OWPDLimDisk;
2799 
2800         if(aval != OWP_CNTRL_ACCEPT){
2801             /*
2802              * The test session was invalid, delete the file,
2803              * and release the resources.
2804              */
2805 
2806             /*
2807              * Unlink the files
2808              */
2809             (void)unlink(finfo->linkpath);
2810             (void)unlink(finfo->filepath);
2811 
2812             assert(tinfo->res[1].limit == OWPDLimDisk);
2813             mesg = OWPDMESGRELEASE;
2814             lim = tinfo->res[1];
2815         }
2816         /*
2817          * Can we release some diskspace from the resource broker?
2818          */
2819         else if(sbuf.st_size < (off_t)tinfo->res[1].value){
2820             mesg = OWPDMESGRELEASE;
2821             lim.value = tinfo->res[1].value - sbuf.st_size;
2822         }
2823         /*
2824          * Ugh. Need to request more... Use "CLAIM" so the
2825          * "diskfudge" factor will be used.
2826          */
2827         else if(sbuf.st_size > (off_t)tinfo->res[1].value){
2828             mesg = OWPDMESGCLAIM;
2829             lim.value = sbuf.st_size - tinfo->res[1].value;
2830         }
2831         /*
2832          * resource is exactly correct - skip resource broker.
2833          */
2834         else{
2835             goto end;
2836         }
2837 
2838         ret = OWPDQuery(finfo->node->policy,mesg,lim);
2839 
2840         /*
2841          * If we were requesting more space, and it was denied,
2842          * unlink the files.
2843          */
2844         if((mesg == OWPDMESGCLAIM) && (ret == OWPDMESGDENIED)){
2845             OWPError(ctx,OWPErrWARNING,OWPErrPOLICY,
2846                     "%s Too large! Deleting... (See diskfudge)",
2847                     finfo->filepath);
2848             (void)unlink(finfo->linkpath);
2849             (void)unlink(finfo->filepath);
2850             /*
2851              * Completely free the resource then.
2852              */
2853             (void)OWPDQuery(finfo->node->policy,OWPDMESGRELEASE,tinfo->res[1]);
2854         }
2855     }
2856     /*
2857      * otherwise - this is a fetch-session target file.
2858      */
2859     else if(rinfo->itype == OWPDINFO_FETCH){
2860         xinfo = &rinfo->test;
2861         finfo = xinfo->finfo;
2862         xinfo->finfo = NULL;
2863 
2864         /*
2865          * Check for the delete_on_fetch option...
2866          *
2867          * Only delete if this fetch was successful for the complete session,
2868          * and the delete_on_fetch option is specified for the
2869          * files limit_class definition.
2870          *
2871          */
2872         if((xinfo->begin == 0) && (xinfo->end == 0xFFFFFFFF) &&
2873                 (aval == OWP_CNTRL_ACCEPT) &&
2874                 GetLimit(finfo->node,OWPDLimDeleteOnFetch)){
2875             /*
2876              * stat the file to determine the size so the resources
2877              * associated with this file can be released.
2878              */
2879             if(fstat(fileno(fp),&sbuf) != 0){
2880                 OWPError(ctx,OWPErrFATAL,errno,
2881                         "OWPDCloseFile: fstat(): %M: Unable to determine filesize...");
2882                 sbuf.st_size = 0;
2883             }
2884 
2885             /*
2886              * Unlink the files
2887              */
2888             (void)unlink(finfo->linkpath);
2889             (void)unlink(finfo->filepath);
2890 
2891             /*
2892              * If we were able to stat - then free the resources.
2893              */
2894             if(sbuf.st_size > 0){
2895                 lim.limit = OWPDLimDisk;
2896                 lim.value = sbuf.st_size;
2897                 (void)OWPDQuery(finfo->node->policy,
2898                                 OWPDMESGRELEASE,lim);
2899             }
2900         }
2901     }
2902 end:
2903     if(tinfo){
2904         tinfo->res[1].limit = 0;
2905         tinfo->res[1].value = 0;
2906     }
2907     if(finfo){
2908         free(finfo);
2909     }
2910     if(xinfo){
2911         free(xinfo);
2912     }
2913     if(fclose(fp) != 0){
2914         OWPError(ctx,OWPErrFATAL,errno,"fclose(): %M");
2915     }
2916 
2917     return;
2918 }
2919