[dovecot-cvs] dovecot/doc securecoding.txt,NONE,1.1

cras at procontrol.fi cras at procontrol.fi
Sun Sep 21 22:03:51 EEST 2003


Update of /home/cvs/dovecot/doc
In directory danu:/tmp/cvs-serv22520

Added Files:
	securecoding.txt 
Log Message:
Secure coding guide for Dovecot.



--- NEW FILE: securecoding.txt ---
Simplicity provides security. The more you have to remember to maintain
security the easier it is to forget something.


Use Multiple Layers of Security
-------------------------------

Input validation is useful to prevent clients from taking too much server
resources. Add the restrictions only where it's useful. For example a
simple "maximum line length" will limit the length of pretty much all
possible client input.

Don't rely on input validation. Maybe you missed something. Maybe someone 
calls your function somewhere else where you didn't originally intend it.
Maybe someone makes the input validation less restrictive for some reason.
Point is, it's not an excuse to cause a security hole just because input
wasn't what you expected it to be.

Don't trust memory. If code somewhere overflowed a buffer, don't make it
easier to exploit it. For example if you have code:

  static char staticbuf[100];
  ..
  char stackbuf[100];
  strcpy(stackbuf, staticbuf);

Just because staticbuf was declared as [100], it doesn't mean it couldn't
contain more data. Overflowing static buffers can't be directly exploited,
but the strcpy() overflowing stackbuf makes it possible. Always copy data
with bounds checking.


Prevent Buffer Overflows
------------------------

Avoid writing to buffers directly. Write everything through buffer API
(lib/buffer.h) which guarantees protection against buffer overflows.
There are various safe string APIs as well (lib/str.h, lib/strfuncs.h).

If you do write to buffers directly, mark the code with /* @UNSAFE */
unless it's _obviously_ safe. Only obviously safe code is calling a
function with (buffer, sizeof(buffer)) parameters. If you do _any_
calculations with buffer size, mark it unsafe.

Use const with buffers whenever you can. It guarantees that you can't
accidentally modify it.

Use "char *" only for NUL-terminated strings. Use "unsigned char *"
if it's not guaranteed to be NUL-terminated.


Avoid free()
------------

Accessing freed memory is the most difficult problem to solve with C code.
Only real solution is to use garbage collector, but it's not possible to
write a portable GC without radical changes in how you write code.

I've added support for Boehm GC, but it doesn't seem to be working very
well currently. In any case I'd rather not make it required.

There are a few ways to avoid most free() calls however: data stack and
memory pools.

Data stack works in somewhat similiar way to C's control stack. alloca() is
quite near to what it does, but there's one major difference: Stack frames
are explicitly defined so functions can return values allocated from data
stack. t_strdup_printf() call is an excellent example of why this is useful.
Rather than creating some arbitrary sized buffer and using snprintf() which
may truncate the value, you can just use t_strdup_printf() without worrying
about buffer sizes being large enough. See lib/data-stack.h

Memory pools are useful when you have to construct an object from multiple
pieces and you can free it all at once. Actually Dovecot's Memory Pool API
is just an abstract class for allocating memory. There's system_pool for
allocating memory with calloc(), realloc() and free() and you can create a
pool to allocate memory from data stack. If your function needs to allocate
memory for multiple objects, you may want to take struct pool as parameter
to allow caller to specify where the memory is allocated from.
See lib/mempool.h


Don't Keep Secrets
------------------

We don't do anything special to protect ourself against read access buffer
overflows, so don't store anything sensitive in memory. We use multiple
processes to protect sensitive information between users.

When dealing with passwords and such, erase them from memory after you
don't need it anymore. Note that such memset() may be optimized away by
compiler, use safe_memset().


Use GCC Extensions
------------------

GCC makes it easy to catch some potential errors:

Format string vulnerabilities can be prevented by marking all functions
using format strings with __attr_format__() and __attr_format_arg__()
macros and using -Wformat=2 GCC option.

-W option checks that you don't compare signed and unsigned variables.

I hope GCC will later emit a warning whenever there's potential integer
truncation. -Wconversion kind of does that, but it's not really meant for
it and it gives too many other useless warnings.


Use union Safely
----------------

Suppose there was code:

union {
   unsigned int number;
   char *str;
} u;

If it was possible for user to set number arbitrarily, but access the union
as string it'd be possible to read or write arbitrary memory locations.

There's two ways to handle this. First would be to avoid union entirely and
use a struct instead. You don't really need the extra few bytes of memory
that union saves.

Another way is to access the union only through macro that verifies that
you're accessing it correctly. See IMAP_ARG_*() macros in
lib-imap/imap-parser.h.



More information about the dovecot-cvs mailing list