1787
|
1 Simplicity provides security. The more you have to remember to maintain
|
|
2 security the easier it is to forget something.
|
|
3
|
|
4
|
|
5 Use Multiple Layers of Security
|
|
6 -------------------------------
|
|
7
|
|
8 Input validation is useful to prevent clients from taking too much server
|
|
9 resources. Add the restrictions only where it's useful. For example a
|
|
10 simple "maximum line length" will limit the length of pretty much all
|
|
11 possible client input.
|
|
12
|
|
13 Don't rely on input validation. Maybe you missed something. Maybe someone
|
|
14 calls your function somewhere else where you didn't originally intend it.
|
|
15 Maybe someone makes the input validation less restrictive for some reason.
|
|
16 Point is, it's not an excuse to cause a security hole just because input
|
|
17 wasn't what you expected it to be.
|
|
18
|
|
19 Don't trust memory. If code somewhere overflowed a buffer, don't make it
|
|
20 easier to exploit it. For example if you have code:
|
|
21
|
|
22 static char staticbuf[100];
|
|
23 ..
|
|
24 char stackbuf[100];
|
|
25 strcpy(stackbuf, staticbuf);
|
|
26
|
|
27 Just because staticbuf was declared as [100], it doesn't mean it couldn't
|
|
28 contain more data. Overflowing static buffers can't be directly exploited,
|
|
29 but the strcpy() overflowing stackbuf makes it possible. Always copy data
|
|
30 with bounds checking.
|
|
31
|
|
32
|
|
33 Prevent Buffer Overflows
|
|
34 ------------------------
|
|
35
|
|
36 Avoid writing to buffers directly. Write everything through buffer API
|
|
37 (lib/buffer.h) which guarantees protection against buffer overflows.
|
|
38 There are various safe string APIs as well (lib/str.h, lib/strfuncs.h).
|
|
39
|
|
40 If you do write to buffers directly, mark the code with /* @UNSAFE */
|
|
41 unless it's _obviously_ safe. Only obviously safe code is calling a
|
|
42 function with (buffer, sizeof(buffer)) parameters. If you do _any_
|
|
43 calculations with buffer size, mark it unsafe.
|
|
44
|
|
45 Use const with buffers whenever you can. It guarantees that you can't
|
|
46 accidentally modify it.
|
|
47
|
|
48 Use "char *" only for NUL-terminated strings. Use "unsigned char *"
|
|
49 if it's not guaranteed to be NUL-terminated.
|
|
50
|
|
51
|
|
52 Avoid free()
|
|
53 ------------
|
|
54
|
|
55 Accessing freed memory is the most difficult problem to solve with C code.
|
|
56 Only real solution is to use garbage collector, but it's not possible to
|
|
57 write a portable GC without radical changes in how you write code.
|
|
58
|
|
59 I've added support for Boehm GC, but it doesn't seem to be working very
|
|
60 well currently. In any case I'd rather not make it required.
|
|
61
|
|
62 There are a few ways to avoid most free() calls however: data stack and
|
|
63 memory pools.
|
|
64
|
|
65 Data stack works in somewhat similiar way to C's control stack. alloca() is
|
|
66 quite near to what it does, but there's one major difference: Stack frames
|
|
67 are explicitly defined so functions can return values allocated from data
|
|
68 stack. t_strdup_printf() call is an excellent example of why this is useful.
|
|
69 Rather than creating some arbitrary sized buffer and using snprintf() which
|
|
70 may truncate the value, you can just use t_strdup_printf() without worrying
|
|
71 about buffer sizes being large enough. See lib/data-stack.h
|
|
72
|
|
73 Memory pools are useful when you have to construct an object from multiple
|
|
74 pieces and you can free it all at once. Actually Dovecot's Memory Pool API
|
|
75 is just an abstract class for allocating memory. There's system_pool for
|
|
76 allocating memory with calloc(), realloc() and free() and you can create a
|
|
77 pool to allocate memory from data stack. If your function needs to allocate
|
|
78 memory for multiple objects, you may want to take struct pool as parameter
|
|
79 to allow caller to specify where the memory is allocated from.
|
|
80 See lib/mempool.h
|
|
81
|
|
82
|
|
83 Don't Keep Secrets
|
|
84 ------------------
|
|
85
|
|
86 We don't do anything special to protect ourself against read access buffer
|
|
87 overflows, so don't store anything sensitive in memory. We use multiple
|
|
88 processes to protect sensitive information between users.
|
|
89
|
|
90 When dealing with passwords and such, erase them from memory after you
|
|
91 don't need it anymore. Note that such memset() may be optimized away by
|
|
92 compiler, use safe_memset().
|
|
93
|
|
94
|
|
95 Use GCC Extensions
|
|
96 ------------------
|
|
97
|
|
98 GCC makes it easy to catch some potential errors:
|
|
99
|
|
100 Format string vulnerabilities can be prevented by marking all functions
|
|
101 using format strings with __attr_format__() and __attr_format_arg__()
|
|
102 macros and using -Wformat=2 GCC option.
|
|
103
|
|
104 -W option checks that you don't compare signed and unsigned variables.
|
|
105
|
|
106 I hope GCC will later emit a warning whenever there's potential integer
|
|
107 truncation. -Wconversion kind of does that, but it's not really meant for
|
|
108 it and it gives too many other useless warnings.
|
|
109
|
|
110
|
|
111 Use union Safely
|
|
112 ----------------
|
|
113
|
|
114 Suppose there was code:
|
|
115
|
|
116 union {
|
|
117 unsigned int number;
|
|
118 char *str;
|
|
119 } u;
|
|
120
|
|
121 If it was possible for user to set number arbitrarily, but access the union
|
|
122 as string it'd be possible to read or write arbitrary memory locations.
|
|
123
|
|
124 There's two ways to handle this. First would be to avoid union entirely and
|
|
125 use a struct instead. You don't really need the extra few bytes of memory
|
|
126 that union saves.
|
|
127
|
|
128 Another way is to access the union only through macro that verifies that
|
|
129 you're accessing it correctly. See IMAP_ARG_*() macros in
|
|
130 lib-imap/imap-parser.h.
|