Berkeley DB reads DB_CONFIG from cwd

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Berkeley DB reads DB_CONFIG from cwd

Scott Kitterman-4
It would be nice to get a read from the Postfix developers if this issue
recently reported to oss-security [1] is relevant to Postfix on systems with
support for Berkeley DB databases?

Scott K

[1] http://www.openwall.com/lists/oss-security/2017/06/10/1
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Berkeley DB reads DB_CONFIG from cwd

Wietse Venema
Scott Kitterman:
> It would be nice to get a read from the Postfix developers if this issue
> recently reported to oss-security [1] is relevant to Postfix on systems with
> support for Berkeley DB databases?

Set-[ug]id privilege escalation with environment settings that
modify the behavior of a library? Not relevant.

Postfix daemon programs must be started by a root-privileged process.
There is no privilege escalation when you're already root.  These
programs import only variables allowed by main.cf:import_environment,
but that is more a matter of hygiene than security.

To avoid privilege escalation, set-gid Postfix non-daemon programs
import only variables that are allowed by main.cf:import_environment.
Here, import_environment is a security feature.

Other Postfix non-daemon programs run with the same privileges as
the process that invokes the command. There is no privilege escalation.

        Wietse
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Berkeley DB reads DB_CONFIG from cwd

Wietse Venema
Wietse Venema:
> Scott Kitterman:
> > It would be nice to get a read from the Postfix developers if this issue
> > recently reported to oss-security [1] is relevant to Postfix on systems with
> > support for Berkeley DB databases?
>
> Set-[ug]id privilege escalation with environment settings that
> modify the behavior of a library? Not relevant.

Hmm. this is about a fixed pathname in the current directory.

Postfix daemons run with cwd == /var/spool/postfix which is writable
only by root. So that is safe.

Set-gid Postfix non-daemon programs will eventually chdir() to
/var/spool/postfix, but it is possible that PAM or NSS opens a db
file before that time, or that postdrop or postqueue open a db file
while initializing some main.cf setting.

I guess that means one could trick Berkeley DB into reading a message
file in the maildrop directory, if you know the maildrop file name.
Normally, a maildrop file will be removed quickly by the pickup
dameon, so I don't know how realistic an attack like this would be.

Other Postfix non-daemon programs run with the same privileges as
the process that invokes the command. There is no privilege escalation.

        Wietse
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Berkeley DB reads DB_CONFIG from cwd

Scott Kitterman-4


On June 10, 2017 9:35:38 PM EDT, [hidden email] wrote:

>Wietse Venema:
>> Scott Kitterman:
>> > It would be nice to get a read from the Postfix developers if this
>issue
>> > recently reported to oss-security [1] is relevant to Postfix on
>systems with
>> > support for Berkeley DB databases?
>>
>> Set-[ug]id privilege escalation with environment settings that
>> modify the behavior of a library? Not relevant.
>
>Hmm. this is about a fixed pathname in the current directory.
>
>Postfix daemons run with cwd == /var/spool/postfix which is writable
>only by root. So that is safe.
>
>Set-gid Postfix non-daemon programs will eventually chdir() to
>/var/spool/postfix, but it is possible that PAM or NSS opens a db
>file before that time, or that postdrop or postqueue open a db file
>while initializing some main.cf setting.
>
>I guess that means one could trick Berkeley DB into reading a message
>file in the maildrop directory, if you know the maildrop file name.
>Normally, a maildrop file will be removed quickly by the pickup
>dameon, so I don't know how realistic an attack like this would be.
>
>Other Postfix non-daemon programs run with the same privileges as
>the process that invokes the command. There is no privilege escalation.

Thanks for reviewing.

Scott K
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Berkeley DB reads DB_CONFIG from cwd

Wietse Venema
Scott Kitterman:

> >Postfix daemons run with cwd == /var/spool/postfix which is writable
> >only by root. So that is safe.
> >
> >Set-gid Postfix non-daemon programs will eventually chdir() to
> >/var/spool/postfix, but it is possible that PAM or NSS opens a db
> >file before that time, or that postdrop or postqueue open a db file
> >while initializing some main.cf setting.
> >
> >I guess that means one could trick Berkeley DB into reading a message
> >file in the maildrop directory, if you know the maildrop file name.
> >Normally, a maildrop file will be removed quickly by the pickup
> >dameon, so I don't know how realistic an attack like this would be.
> >
> >Other Postfix non-daemon programs run with the same privileges as
> >the process that invokes the command. There is no privilege escalation.
>
> Thanks for reviewing.

Oh, and it will of course open a DB_CONFIG file in whatever happens
to be the super-user's cwd when they invoke the postmap or postalias
command, so this is not just a matter of set-gid Postfix commands.

Although opening a DB_CONFIG file in the current directory is
undocumented, there is prior art fixing Berkeley DB callers instead
Berkeley DB, for example the fix for nss_db (CVE-2010-0826).

Having rolled a new release this Saturday, I can save people some
time by rolling out another one today, based on the nss_db fix.
No point reinventing this.

        Wietse

--- /var/tmp/postfix-3.3-20170610/src/util/dict_db.c 2014-12-06 20:35:33.000000000 -0500
+++ src/util/dict_db.c 2017-06-11 13:29:22.581994051 -0400
@@ -122,6 +122,9 @@
 typedef struct {
     DICT    dict; /* generic members */
     DB     *db; /* open db file */
+#if DB_VERSION_MAJOR > 2
+    DB_ENV *dbenv;
+#endif
 #if DB_VERSION_MAJOR > 1
     DBC    *cursor; /* dict_db_sequence() */
 #endif
@@ -553,6 +556,9 @@
     if (DICT_DB_CLOSE(dict_db->db) < 0)
  msg_info("close database %s: %m (possible Berkeley DB bug)",
  dict_db->dict.name);
+#if DB_VERSION_MAJOR > 2
+    dict_db->dbenv->close(dict_db->dbenv, 0);
+#endif
     if (dict_db->key_buf)
  vstring_free(dict_db->key_buf);
     if (dict_db->val_buf)
@@ -578,6 +584,11 @@
     int     db_flags;
 
 #endif
+#if DB_VERSION_MAJOR > 2
+    DB_ENV *dbenv;
+    VSTRING *dirname_buf;
+
+#endif
 
     /*
      * Mismatches between #include file and library are a common cause for
@@ -681,12 +692,18 @@
  db_flags |= DB_CREATE;
     if (open_flags & O_TRUNC)
  db_flags |= DB_TRUNCATE;
-    if ((errno = db_create(&db, 0, 0)) != 0)
+    /* Fix 20170611 workaround for undocumented ./DB_CONFIG read. */
+    if ((errno = db_env_create(&dbenv, 0)) != 0)
+ msg_fatal("create DB environment: %m");
+    dirname_buf = vstring_alloc(100);
+    if ((errno = dbenv->open(dbenv, sane_dirname(dirname_buf, db_path),
+   DB_INIT_MPOOL | DB_CREATE | DB_PRIVATE, 0)) != 0)
+ msg_fatal("open DB environment: %m");
+    vstring_free(dirname_buf);
+    if ((errno = db_create(&db, dbenv, 0)) != 0)
  msg_fatal("create DB database: %m");
     if (db == 0)
  msg_panic("db_create null result");
-    if ((errno = db->set_cachesize(db, 0, dict_db_cache_size, 0)) != 0)
- msg_fatal("set DB cache size %d: %m", dict_db_cache_size);
     if (type == DB_HASH && db->set_h_nelem(db, DICT_DB_NELM) != 0)
  msg_fatal("set DB hash element count %d: %m", DICT_DB_NELM);
 #if DB_VERSION_MAJOR == 6 || DB_VERSION_MAJOR == 5 || \
@@ -743,6 +760,7 @@
     if (dict_flags & DICT_FLAG_FOLD_FIX)
  dict_db->dict.fold_buf = vstring_alloc(10);
     dict_db->db = db;
+    dict_db->dbenv = dbenv;
 #if DB_VERSION_MAJOR > 1
     dict_db->cursor = 0;
 #endif
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Berkeley DB reads DB_CONFIG from cwd

Philip Paeps
On 2017-06-11 14:07:36 (-0400), Wietse Venema <[hidden email]> wrote:
>Oh, and it will of course open a DB_CONFIG file in whatever happens to
>be the super-user's cwd when they invoke the postmap or postalias
>command, so this is not just a matter of set-gid Postfix commands.
>
>[...]

>-    if ((errno = db->set_cachesize(db, 0, dict_db_cache_size, 0)) != 0)
>- msg_fatal("set DB cache size %d: %m", dict_db_cache_size);

Is this change intentional, or did it sneak in?  It seems unrelated to
the environment workaround.

Philip

--
Philip Paeps
Senior Reality Engineer
Ministry of Information
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Berkeley DB reads DB_CONFIG from cwd

Wietse Venema
Philip Paeps:

> On 2017-06-11 14:07:36 (-0400), Wietse Venema <[hidden email]> wrote:
> >Oh, and it will of course open a DB_CONFIG file in whatever happens to
> >be the super-user's cwd when they invoke the postmap or postalias
> >command, so this is not just a matter of set-gid Postfix commands.
> >
> >[...]
>
> >-    if ((errno = db->set_cachesize(db, 0, dict_db_cache_size, 0)) != 0)
> >- msg_fatal("set DB cache size %d: %m", dict_db_cache_size);
>
> Is this change intentional, or did it sneak in?  It seems unrelated to
> the environment workaround.

dbenv->set_cachesize(dbenv, 0, dict_db_cache_size, 0) rejected a
call with the Postfix default cache size, and there appears to be
no API to find what cache size it would have accepted. Screw it.
This library needs to die.

        Wietse
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Berkeley DB reads DB_CONFIG from cwd

Wietse Venema
In reply to this post by Wietse Venema
> Oh, and it will of course open a DB_CONFIG file in whatever happens
> to be the super-user's cwd when they invoke the postmap or postalias
> command, so this is not just a matter of set-gid Postfix commands.
>
> Although opening a DB_CONFIG file in the current directory is
> undocumented, there is prior art fixing Berkeley DB callers instead
> Berkeley DB, for example the fix for nss_db (CVE-2010-0826).
>
> Having rolled a new release this Saturday, I can save people some
> time by rolling out another one today, based on the nss_db fix.
> No point reinventing this.

The dev release is postfix-3.3-20170611. I'll roll out stable
releases for 2.11 .. 3.2 after a cool-down period. Sofar it passes
limited tests with Berkeley DB 5.3 (Linux x86-64), and Berkeley DB
4.7 and 6.0 (FreeBSD amd64).

        Wietse
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Berkeley DB reads DB_CONFIG from cwd

Matthias Andree
In reply to this post by Wietse Venema
Am 11.06.2017 um 20:50 schrieb Wietse Venema:

> Philip Paeps:
>> On 2017-06-11 14:07:36 (-0400), Wietse Venema <[hidden email]> wrote:
>>> Oh, and it will of course open a DB_CONFIG file in whatever happens to
>>> be the super-user's cwd when they invoke the postmap or postalias
>>> command, so this is not just a matter of set-gid Postfix commands.
>>>
>>> [...]
>>> -    if ((errno = db->set_cachesize(db, 0, dict_db_cache_size, 0)) != 0)
>>> - msg_fatal("set DB cache size %d: %m", dict_db_cache_size);
>> Is this change intentional, or did it sneak in?  It seems unrelated to
>> the environment workaround.
> dbenv->set_cachesize(dbenv, 0, dict_db_cache_size, 0) rejected a
> call with the Postfix default cache size, and there appears to be
> no API to find what cache size it would have accepted. Screw it.
> This library needs to die.

Hi Wietse,

sorry for the techy noise on a users list in this post. There may be
valid reasons for killing off Berkeley DB, but your using it counter to
the specs is not one :-)

Explanation: BDB did not reject the size, but it did reject your attempt
to call db->set_cachesize(...) when the "db" handle was created and
initialized with a non-NULL dbenv (the db_create(&db, dbenv, 0) call below).

Meaning that:
You are attempting to set the cache size on the /database/ (not the
/environment/). The documentation of Berkeley DB states it is an error
to set the cache size for databases opened within an environment (i. e.
with a non-null dbenv used in db_create()), because the database uses
the cache from its environment. Before your patch, there was no
environment (the dbenv argument was 0 in your db_create() call), so it
was permissible to call db->set_cachesize().

So instead of calling db->set_cachesize, you now - after introducing the
environment - need to call dbenv->set_cachesize, (the _env_ being the
important addition), so that you set the cache size on the environment
(dbenv variable in your code) instead.
We've been doing that successfully in bogofilter for more than a decade
across various Berkeley DB versions.

You can no longer use this:
*
https://docs.oracle.com/cd/E17275_01/html/api_reference/C/dbset_cachesize.html
"Because databases opened within Berkeley DB environments use the cache
specified to the environment, it is an error to attempt to set a cache
in a database created within an environment."

You need to use this:
*
https://docs.oracle.com/cd/E17275_01/html/api_reference/C/envset_cachesize.html

and you best set it very early, *before* the dbenv->open.


Note that the fatal error messages in the code are also misleading here
in two places - all these marked calls create are the in-memory
structures ("handles"), the actual environment or database, are only
created on their respective ->open() calls.
> +    /* Fix 20170611 workaround for undocumented ./DB_CONFIG read. */
> +    if ((errno = db_env_create(&dbenv, 0)) != 0)
> + msg_fatal("create DB environment: %m");
here ^^^^^^ I suggest:

msg_fatal("create DB environment handle: %m");

> +    dirname_buf = vstring_alloc(100);
> +    if ((errno = dbenv->open(dbenv, sane_dirname(dirname_buf, db_path),
> +   DB_INIT_MPOOL | DB_CREATE | DB_PRIVATE, 0)) != 0)
> + msg_fatal("open DB environment: %m");
> +    vstring_free(dirname_buf);
> +    if ((errno = db_create(&db, dbenv, 0)) != 0)
>   msg_fatal("create DB database: %m");

here ^^^^^^ I suggest:

msg_fatal("create DB database handle: %m");

HTH
Matthias

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Berkeley DB reads DB_CONFIG from cwd

Wietse Venema
Matthias Andree:

> Am 11.06.2017 um 20:50 schrieb Wietse Venema:
> > Philip Paeps:
> >> On 2017-06-11 14:07:36 (-0400), Wietse Venema <[hidden email]> wrote:
> >>> Oh, and it will of course open a DB_CONFIG file in whatever happens to
> >>> be the super-user's cwd when they invoke the postmap or postalias
> >>> command, so this is not just a matter of set-gid Postfix commands.
> >>>
> >>> [...]
> >>> -    if ((errno = db->set_cachesize(db, 0, dict_db_cache_size, 0)) != 0)
> >>> - msg_fatal("set DB cache size %d: %m", dict_db_cache_size);
> >> Is this change intentional, or did it sneak in?  It seems unrelated to
> >> the environment workaround.
> > dbenv->set_cachesize(dbenv, 0, dict_db_cache_size, 0) rejected a
> > call with the Postfix default cache size, and there appears to be
> > no API to find what cache size it would have accepted. Screw it.
> > This library needs to die.
>
> Hi Wietse,
>
> sorry for the techy noise on a users list in this post. There may be
> valid reasons for killing off Berkeley DB, but your using it counter to
> the specs is not one :-)
>
> Explanation: BDB did not reject the size, but it did reject your attempt
> to call db->set_cachesize(...) when the "db" handle was created and
> initialized with a non-NULL dbenv (the db_create(&db, dbenv, 0) call below).

I was calling dbenv->set_cachesize(dbenv, 0, dict_db_cache_size, 0)
as stated in my email above, not db->set_cachesize(...)

Anyway.  I found a call dbenv->set_cache_max, introduced somewhere
between DB 4.2 and DB 4.7. That fixes the problem for all but very
old implementations. See postfix-3.3-20170612.

        Wietse

> Meaning that:
> You are attempting to set the cache size on the /database/ (not the
> /environment/). The documentation of Berkeley DB states it is an error
> to set the cache size for databases opened within an environment (i. e.
> with a non-null dbenv used in db_create()), because the database uses
> the cache from its environment. Before your patch, there was no
> environment (the dbenv argument was 0 in your db_create() call), so it
> was permissible to call db->set_cachesize().
>
> So instead of calling db->set_cachesize, you now - after introducing the
> environment - need to call dbenv->set_cachesize, (the _env_ being the
> important addition), so that you set the cache size on the environment
> (dbenv variable in your code) instead.
> We've been doing that successfully in bogofilter for more than a decade
> across various Berkeley DB versions.
>
> You can no longer use this:
> *
> https://docs.oracle.com/cd/E17275_01/html/api_reference/C/dbset_cachesize.html
> "Because databases opened within Berkeley DB environments use the cache
> specified to the environment, it is an error to attempt to set a cache
> in a database created within an environment."
>
> You need to use this:
> *
> https://docs.oracle.com/cd/E17275_01/html/api_reference/C/envset_cachesize.html
>
> and you best set it very early, *before* the dbenv->open.
>
>
> Note that the fatal error messages in the code are also misleading here
> in two places - all these marked calls create are the in-memory
> structures ("handles"), the actual environment or database, are only
> created on their respective ->open() calls.
> > +    /* Fix 20170611 workaround for undocumented ./DB_CONFIG read. */
> > +    if ((errno = db_env_create(&dbenv, 0)) != 0)
> > + msg_fatal("create DB environment: %m");
> here ^^^^^^ I suggest:
>
> msg_fatal("create DB environment handle: %m");
>
> > +    dirname_buf = vstring_alloc(100);
> > +    if ((errno = dbenv->open(dbenv, sane_dirname(dirname_buf, db_path),
> > +   DB_INIT_MPOOL | DB_CREATE | DB_PRIVATE, 0)) != 0)
> > + msg_fatal("open DB environment: %m");
> > +    vstring_free(dirname_buf);
> > +    if ((errno = db_create(&db, dbenv, 0)) != 0)
> >   msg_fatal("create DB database: %m");
>
> here ^^^^^^ I suggest:
>
> msg_fatal("create DB database handle: %m");
>
> HTH
> Matthias
>
>
        Wietse
Loading...