IMAP in OCaml
1# imapd
2
3An IMAP4rev2 server implemented in OCaml.
4
5Implements [RFC 9051](https://datatracker.ietf.org/doc/html/rfc9051) (IMAP4rev2) with support for implicit TLS per [RFC 8314](https://datatracker.ietf.org/doc/html/rfc8314).
6
7## Features
8
9- **IMAP4rev2** (RFC 9051) with IMAP4rev1 compatibility
10- **Fork-per-connection** privilege separation (like UW-IMAP)
11- **Implicit TLS** on port 993 (RFC 8314)
12- **STARTTLS** upgrade for cleartext connections
13- **PAM authentication** using system accounts
14- **Maildir storage** with traditional `~/Maildir` layout
15- **In-memory storage** for development and testing
16
17### Supported Extensions
18
19| Extension | RFC | Description |
20|-----------|-----|-------------|
21| IDLE | [RFC 2177](https://datatracker.ietf.org/doc/html/rfc2177) | Real-time notifications |
22| NAMESPACE | [RFC 2342](https://datatracker.ietf.org/doc/html/rfc2342) | Mailbox namespaces |
23| ID | [RFC 2971](https://datatracker.ietf.org/doc/html/rfc2971) | Server identification |
24| UIDPLUS | [RFC 4315](https://datatracker.ietf.org/doc/html/rfc4315) | UID responses for COPY/APPEND |
25| ENABLE | [RFC 5161](https://datatracker.ietf.org/doc/html/rfc5161) | Capability negotiation |
26| MOVE | [RFC 6851](https://datatracker.ietf.org/doc/html/rfc6851) | Atomic move operation |
27| LITERAL+ | [RFC 7888](https://datatracker.ietf.org/doc/html/rfc7888) | Non-synchronizing literals |
28
29## Installation
30
31### Prerequisites
32
33- OCaml 5.0+
34- opam
35- PAM development headers (`libpam0g-dev` on Debian/Ubuntu)
36
37### Building
38
39```bash
40opam install . --deps-only
41dune build
42```
43
44### Running Tests
45
46```bash
47dune test
48```
49
50## Usage
51
52### Development Server
53
54```bash
55# In-memory storage on port 10143
56imapd -s memory -p 10143
57```
58
59### Production Server (Recommended)
60
61```bash
62# Fork-per-connection with implicit TLS
63# Uses ~/Maildir for each user
64sudo imapd --fork -s maildir --tls \
65 --cert /etc/ssl/certs/mail.crt \
66 --key /etc/ssl/private/mail.key \
67 -p 993
68```
69
70### Single-Process with STARTTLS
71
72```bash
73# Cleartext with STARTTLS upgrade
74imapd -s maildir --maildir-path /var/mail \
75 --cert server.crt --key server.key \
76 -p 143
77```
78
79## Operating Modes
80
81### Single-Process (default)
82
83All connections handled in one process. Efficient but all sessions share the same privileges. Suitable for development or trusted environments.
84
85### Fork-per-Connection (`--fork`)
86
87Each connection forks a child process. After successful authentication, the child drops privileges to the authenticated user via `setuid`. This provides strong isolation between users.
88
89- Requires running as root
90- Only works with Maildir storage
91- STARTTLS not supported (use implicit TLS)
92
93## Storage Backends
94
95### Memory
96
97In-memory storage for development and testing. Data is not persisted.
98
99```bash
100imapd -s memory
101```
102
103### Maildir
104
105Production storage using the [Maildir format](https://cr.yp.to/proto/maildir.html).
106
107**With shared base path:**
108```bash
109imapd -s maildir --maildir-path /var/mail
110# Mail stored at /var/mail/<username>/
111```
112
113**With home directories (fork mode default):**
114```bash
115sudo imapd --fork -s maildir
116# Mail stored at ~<username>/Maildir/
117```
118
119## Command-Line Options
120
121| Option | Description |
122|--------|-------------|
123| `-p`, `--port` | Port to listen on (default: 143) |
124| `-h`, `--host` | Host address to bind to (default: 127.0.0.1) |
125| `-s`, `--storage` | Storage backend: `memory` or `maildir` |
126| `--maildir-path` | Base path for Maildir storage |
127| `--tls` | Enable implicit TLS (requires `--cert` and `--key`) |
128| `--cert` | TLS certificate file (PEM format) |
129| `--key` | TLS private key file (PEM format) |
130| `--fork` | Fork per connection with privilege separation |
131
132## Architecture
133
134```
135imapd/
136├── lib/
137│ ├── imap_types/ # Core IMAP types (RFC 9051)
138│ ├── imap_parser/ # Menhir parser + Faraday serializer
139│ ├── imap_auth/ # PAM authentication
140│ ├── imap_storage/ # Memory and Maildir backends
141│ └── imap_server/ # Connection handler and state machine
142├── bin/
143│ └── main.ml # CLI entry point
144└── test/ # Alcotest test suite
145```
146
147### Connection State Machine
148
149```
150┌─────────────────────┐
151│ Not Authenticated │ ← Initial state
152├─────────────────────┤
153│ CAPABILITY, NOOP │
154│ STARTTLS, LOGIN │
155│ LOGOUT │
156└─────────┬───────────┘
157 │ LOGIN success
158 ▼
159┌─────────────────────┐
160│ Authenticated │
161├─────────────────────┤
162│ SELECT, EXAMINE │
163│ CREATE, DELETE │
164│ LIST, STATUS │
165│ APPEND, IDLE │
166└─────────┬───────────┘
167 │ SELECT success
168 ▼
169┌─────────────────────┐
170│ Selected │
171├─────────────────────┤
172│ FETCH, STORE │
173│ SEARCH, COPY, MOVE │
174│ EXPUNGE, CLOSE │
175└─────────────────────┘
176```
177
178## Security
179
180- **Privilege separation**: Fork mode drops to authenticated user via `setuid`
181- **Path traversal protection**: Username and mailbox names are validated
182- **DoS mitigation**: Maximum line length enforced (64KB)
183- **TLS**: Implicit TLS recommended for production
184
185## Testing with a Client
186
187```bash
188# Start development server
189imapd -s memory -p 10143 &
190
191# Connect with OpenSSL (cleartext for testing)
192telnet localhost 10143
193
194# Or with TLS
195openssl s_client -connect localhost:993
196```
197
198Example session:
199```
200* OK [CAPABILITY IMAP4rev2 ...] IMAP4rev2 Service Ready
201a001 LOGIN username password
202a001 OK [CAPABILITY ...] LOGIN completed
203a002 SELECT INBOX
204* FLAGS (\Seen \Answered \Flagged \Deleted \Draft)
205* 0 EXISTS
206* OK [UIDVALIDITY 1234567890] UIDs valid
207a002 OK [READ-WRITE] SELECT completed
208a003 LOGOUT
209* BYE IMAP4rev2 Server logging out
210a003 OK LOGOUT completed
211```
212
213## License
214
215ISC License. See [LICENSE.md](LICENSE.md) for details.
216
217## Contributing
218
219Report bugs at https://tangled.org/@anil.recoil.org/ocaml-imap/issues