simple CalDAV server
CGI program minimally implements
CalDAV, with an emphasis on strong authorisation and simplicity. To use
, you'll need to first install a
calendar database in the compile-time root directory
. The root directory is relative
to the CGI script's file-system root.
Once installed, you can point your calendar client to owned collections or the
calendar home (depending on the client) within the calendar root.
For example, assume the script has been installed into
and is reachable at
Moreover, assume your calendar root /caldav
is based in the web server's chroot(2)
. It contains a
specifying a single principal
having a single calendar
. This can be created
# kcaldav.passwd -C -u
If your CalDAV client supports the calendar-home-set feature, you can point it
to the principal URL:
If the calendar-home-set feature is not supported by your client (e.g., with
Thunderbird), you may need to point directly to the managed calendar:
A simpler setup might occur all in one directory, such as fully installing into
the user directory, public_html
, of a given
user. Here I assume that the directory has CGI-execute permissions set and the
CGI script kcaldav.cgi
and its database
installed into /home/foo/public_html
The user can then access calenders on
In either case, please use HTTPS! Although
uses HTTP authentication to the
fullest, that still doesn't protect you from snooping.
Many modern calendar clients use “well-known” URI identifiers to
query a host for its CalDAV. To enable the “well-known” service
, the web server's absolute path
must return a 301
redirect to the absolute URL
as the CalDAV host.
Let the root directory be /caldav
directory within the CGI script's chroot(2)
file-system, which is read-writable by the CGI script running as user www. Let
the dummy user kcaldav be the owner of kcaldav files. First make
set-user-ID for modifying the
# chown kcaldav /usr/local/bin/kcaldav.passwd
# chmod u+s /usr/local/bin/kcaldav.passwd
Create user principals as follows:
# kcaldav.passwd -f /caldav -C -u user1
# kcaldav.passwd -f /caldav -C -u user2
Now any local user can modify her principal's password without administrator
user1% kcaldav.passwd -f
Let the root directory be /home/foo/caldav
which is readable by the web server running as user www and read-writable by
the logged-in user foo. First, create the database and add principals as
% kcaldav.passwd -C -u foo -f
has built-in support for web-based
administration by logged-in (using HTTP authentication) principals through a
as a JSON backend. Let's assume
these page have been installed into
. Principals can
then access the server root,
to see their configuration.
The principal foo can modify collection information online.
: you will need to make sure that the
calendar database is read-writable by the web server. In general, the
directory containing the database will also need to be writable by the web
server for creating its temporary transaction files.
In this section, I describe each HTTP method supported.
The DELETE method is handled according to RFC 4918. Principals must have
access. Resource deletions process
the “If-Match” header, if applied, for conditional deletion.
Collection deletions begin by removing the
file, which renders the
directory opaque to
that, all files are removed followed by the directory itself.
: it is possible to remove the principal's
The GET method is supported for calendar resources (defined by content-type) as
defined by RFC 4918. The “If-None-Match” header is processed and
checks the file etag (MD5 sum). In all cases, the principal must have been
delegated read access.
For the JSON content-type (i.e., web application resources),
routes into a management framework.
Only the index.html
resource in the server
root directory is supported. Requests for the empty root (“\”)
are aliased to index.html
The POST method is supported only for text/html requests for specific management
(JSON content-type) resources. POST methods on calendar collections are
interpreted by the management framework as JSON form requests to change
collection properties. The principal must have been delegated write access.
The PROPFIND method queries collection or resource properties. The principal
must have been delegated read access. It accepts the “Depth”
header for recursive reports. The following is a table of get-able properties.
||RFC 4791, 9.6
||RFC 4791, 5.2.1
||RFC 4791, 6.2.1
||RFC 4791, 5.2.2
||RFC 6638, 2.4.1
||RFC 5379, 3
||RFC 3744, 5.4
||RFC 4918, 15.2
||RFC 4918, 15.5
||RFC 4918, 15.6
||RFC 3744, 4.3; caldav-proxy.txt
||RFC 3744, 4.4; caldav-proxy.txt
||RFC 4791, 5.2.6
||RFC 4918, 14.17
||RFC 3744, 4.2
||RFC 4331, 3
||RFC 4331, 4
||RFC 4918, 15.9
||RFC 6638, 9.1
||RFC 4791, 5.2.3
||RFC 4791, 9.6
If the web server has write access to collection
files and the principal has
been delegated write access, its properties may be modified. The following is
a table of settable properties.
||RFC 4791, 5.2.1
||RFC 4918, 15.2
The PUT method is supported for calendar resources where the principal has been
delegated write access.
The “If-Match” and “If” headers are both accepted to
check against etags (MD5 sums) and conditionally replace resources.
The REPORT method is handled similarly to
. It accepts the
“Depth” header for recursive reports.
system is fairly complicated,
though as simple as it can be. It focusses on safety and security throughout.
In this section, I describe several important topics regarding implementation.
requires HTTP “QOP”
digest authentication. A nonce database
is maintained in the root
calendar directory, consisting of nonce values and their current nonce counts.
file is bound in size,
with requests for new nonces evicting the oldest request. The use of nonces
and nonce counts guarantees that principals are not subject to replay attacks.
Nonces are 16-bytes of random data from
To protect against attackers starving the nonce database by endlessly requesting
nonces (evicting valid nonces),
uses a series of checks.
When a client first accesses the system (without authentication), it is given a
random, unrecorded nonce.
When the client re-authenticates using the random nonce and principal
credentials, the system first checks that the user is valid. The nonce is then
checked in the database. If it is not found (the case for principals
re-authenticating with the random nonce), authentication is requested again
with the “stale” directive and a new nonce entry in the
database. Replay nonces request a full re-authentication. This step ensures
that the principal is valid, though it could be a replay attack from a nonce
entry since evicted.
Finally, the client re-authenticates with the recorded nonce and is able to
access the system.
The remaining attack is for an adversary to build up a database of known
historical responses and replay them all at once.
Well-defined calendar date and time is required for computing ranges of
free-busy, multiget filters, and so on.
parses valid RFC 2445 (iCalendar)
calendar dates fully, encompassing arbitrary repeat-rules and so on. Parsing
UTC time-stamps is well-defined using the formula from the “Single Unix
Specification” section 15 on “Seconds since epoch”.
Parsing embedded time-zone time-stamps is far more complicated, but fully
supported as defined by RFC 2445 using both the SUS algorithm and Zeller's
congruence to compute time components. Other CalDAV implementations make use
assumes it is in a
and that this database is unavailable,
it parses all time-zone definitions directly.
One of the most complex components of RFC 2445 is the repeat-rule, such as that
used for time-zone daylight and standard sub-component definitions.
enumerates over all possible
repeat-rule instances, and is thus able to accomodate for
arbitrarily-complicated repeat rules.
utility minimally implements RFC
4918 (WebDAV), RFC 4791 (CalDAV), and of course RFC 2616 (HTTP). It also
implements the following extensions:
- The “ctag” Calendar Server Extension.
- Read-write delegation support.
- RFC 2617
- “Digest” authentication of all users.
- RFC 3744
- ACL queries on the authenticated principal (not ACEs).
- RFC 4331
- Available and used bytes in the collection file-system via
- RFC 5397
- The current principal address.
- RFC 7232
- Conditional HTTP responses (etag, “If-Match”, etc.).
The “well-known” interface is documented in RFC 5785.