Re: pledge(2) API ideas for libraries

2016-06-13 Thread Ray Lai
pledge should be used to restrict a program to whatever it is necessary to do,
rather than everything the library can do. So if I use libimaginarydb to parse
a csv file I've already read into a memory buffer (nearly pledge("", NULL)),
but the library can read/write/create files, do remote db connections, and
fork shells, we don't want it to do all that (pledge("rpath wpath cpath dns
inet exec proc", NULL)).

> On Jun 13, 2016, at 12:56 PM, 
 wrote:
>
> I have thought of a way pledge(2) can be made a little more
> library-friendly.
>
> This is not a patch, but just a thought.
> There are 2 setups I have thought of:
>
> === 1. Variable arguments ===
>
> int pledge(const char *promises, const char *paths[])
> {
>return vpledge(1, promises, paths);
> }
>
> int vpledge(const size_t npledge, ...);
>
> -
>
> In a program, this may be something like this:
>
> #include 
> #include 
> #include 
> #include 
> #include 
> #include 
>
> int main(void) {
>if(vpledge(5, "stdio rpath wpath cpath", NULL,
>ultra_promises, ultra_pledgepaths,
>extra_promises, NULL, super_promises, NULL,
>mecha_promises, mecha_pledgepaths) == -1)
>errx("pledge");
>
>... [other code] ...
> };
>
>
> ---
>
> In vpledge(), "npledge" refers to the number of pledge-pairs, which
> consist of:
>
>const char *promises, const char *paths[]
>
> These have the same semantics as the original pledge().
>
> A library can export *_promises and *_pledgepaths symbols, pointing to
> static text. This allows the library to change without the program
> being affected because the new library changes call something outside
> the original pledge() of a program.
>
> = 2. Using a struct ==
>
> -
>
> struct pledge {
>char *promises;
>char *paths[];
> };
>
> -
>
> int pledge(const char *promises, const char *paths[])
> {
>struct pledge pl = {
>.promises, paths
>};
>
>return pledges(1, &pl);
> }
>
> int pledges(const size_t npledge, const struct pledge pledge_array[]);
>
> -
>
> In a program, this may be something like this:
>
> #include 
> #include 
> #include 
> #include 
> #include 
> #include 
>
> int main(void) {
>struct pledge pl[4];
>
>pl[0].promises = "stdio rpath wpath cpath";
>ultra_getpledge(&pl[1]);
>extra_getpledge(&pl[2]);
>super_getpledge(&pl[3]);
>mecha_getpledge(&pl[4]);
>
>if(pledges(5, pl) == -1)
>errx("pledge");
>
>... [other code] ...
>
>
> };
>
>
> ---
>
>
> A library can tell the application what pledges are in use as follows:
>
>
> static const char *pledge_promises = "stdio fattr sendfd recvfd"
>
> void ultra_getpledge(struct pledge *const pl)
> {
>pl->promises = pledge_promises;
>pl->paths = NULL;
> }
>
>
> ==
>
> I think that #1 has the advantage of it being easier to code so a
> program can ratchet down its abilities. #2 allows one to group the
> pledge arguments into a single struct.
>
> Thoughts?



pledge(2) API ideas for libraries

2016-06-12 Thread bytevolcano
I have thought of a way pledge(2) can be made a little more
library-friendly.

This is not a patch, but just a thought.
There are 2 setups I have thought of:

=== 1. Variable arguments ===

int pledge(const char *promises, const char *paths[])
{
return vpledge(1, promises, paths);
}

int vpledge(const size_t npledge, ...);

-

In a program, this may be something like this:

#include 
#include 
#include 
#include 
#include 
#include 

int main(void) {
if(vpledge(5, "stdio rpath wpath cpath", NULL,
ultra_promises, ultra_pledgepaths,
extra_promises, NULL, super_promises, NULL,
mecha_promises, mecha_pledgepaths) == -1)
errx("pledge");

... [other code] ...
};


 ---

In vpledge(), "npledge" refers to the number of pledge-pairs, which
consist of:

const char *promises, const char *paths[]

These have the same semantics as the original pledge().

A library can export *_promises and *_pledgepaths symbols, pointing to
static text. This allows the library to change without the program
being affected because the new library changes call something outside
the original pledge() of a program.

= 2. Using a struct ==

-

struct pledge {
char *promises;
char *paths[];
};

-

int pledge(const char *promises, const char *paths[])
{
struct pledge pl = {
.promises, paths
};

return pledges(1, &pl);
}

int pledges(const size_t npledge, const struct pledge pledge_array[]);

-

In a program, this may be something like this:

#include 
#include 
#include 
#include 
#include 
#include 

int main(void) {
struct pledge pl[4];

pl[0].promises = "stdio rpath wpath cpath";
ultra_getpledge(&pl[1]);
extra_getpledge(&pl[2]);
super_getpledge(&pl[3]);
mecha_getpledge(&pl[4]);

if(pledges(5, pl) == -1)
errx("pledge");

... [other code] ...


};


 ---


A library can tell the application what pledges are in use as follows:


static const char *pledge_promises = "stdio fattr sendfd recvfd"

void ultra_getpledge(struct pledge *const pl)
{
pl->promises = pledge_promises;
pl->paths = NULL;
}


==

I think that #1 has the advantage of it being easier to code so a
program can ratchet down its abilities. #2 allows one to group the
pledge arguments into a single struct.

Thoughts?