1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
7 Copyright 2012 Zbigniew Jędrzejewski-Szmek
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include "microhttpd-util.h"
34 #include <gnutls/gnutls.h>
35 #include <gnutls/x509.h>
38 void microhttpd_logger(void *arg, const char *fmt, va_list ap) {
41 f = strappenda("microhttpd: ", fmt);
43 DISABLE_WARNING_FORMAT_NONLITERAL;
44 log_metav(LOG_INFO, NULL, 0, NULL, f, ap);
49 static int mhd_respond_internal(struct MHD_Connection *connection,
50 enum MHD_RequestTerminationCode code,
53 enum MHD_ResponseMemoryMode mode) {
54 struct MHD_Response *response;
59 response = MHD_create_response_from_buffer(size, buffer, mode);
63 log_debug("Queing response %u: %s", code, buffer);
64 MHD_add_response_header(response, "Content-Type", "text/plain");
65 r = MHD_queue_response(connection, code, response);
66 MHD_destroy_response(response);
71 int mhd_respond(struct MHD_Connection *connection,
72 enum MHD_RequestTerminationCode code,
73 const char *message) {
75 return mhd_respond_internal(connection, code,
76 (char*) message, strlen(message),
77 MHD_RESPMEM_PERSISTENT);
80 int mhd_respond_oom(struct MHD_Connection *connection) {
81 return mhd_respond(connection, MHD_HTTP_SERVICE_UNAVAILABLE, "Out of memory.\n");
84 int mhd_respondf(struct MHD_Connection *connection,
85 enum MHD_RequestTerminationCode code,
86 const char *format, ...) {
96 r = vasprintf(&m, format, ap);
100 return respond_oom(connection);
102 return mhd_respond_internal(connection, code, m, r, MHD_RESPMEM_MUST_FREE);
108 const char *const names[4];
111 } gnutls_log_map[] = {
112 { {"0"}, LOG_DEBUG },
113 { {"1", "audit"}, LOG_WARNING, true}, /* gnutls session audit */
114 { {"2", "assert"}, LOG_DEBUG }, /* gnutls assert log */
115 { {"3", "hsk", "ext"}, LOG_DEBUG }, /* gnutls handshake log */
116 { {"4", "rec"}, LOG_DEBUG }, /* gnutls record log */
117 { {"5", "dtls"}, LOG_DEBUG }, /* gnutls DTLS log */
118 { {"6", "buf"}, LOG_DEBUG },
119 { {"7", "write", "read"}, LOG_DEBUG },
120 { {"8"}, LOG_DEBUG },
121 { {"9", "enc", "int"}, LOG_DEBUG },
124 void log_func_gnutls(int level, const char *message) {
127 if (0 <= level && level < (int) ELEMENTSOF(gnutls_log_map)) {
128 if (gnutls_log_map[level].enabled)
129 log_meta(gnutls_log_map[level].level, NULL, 0, NULL,
130 "gnutls %d/%s: %s", level, gnutls_log_map[level].names[1], message);
132 log_debug("Received GNUTLS message with unknown level %d.", level);
133 log_meta(LOG_DEBUG, NULL, 0, NULL, "gnutls: %s", message);
137 int log_enable_gnutls_category(const char *cat) {
140 if (streq(cat, "all")) {
141 for (i = 0; i < ELEMENTSOF(gnutls_log_map); i++)
142 gnutls_log_map[i].enabled = true;
143 log_reset_gnutls_level();
146 for (i = 0; i < ELEMENTSOF(gnutls_log_map); i++)
147 if (strv_contains((char**)gnutls_log_map[i].names, cat)) {
148 gnutls_log_map[i].enabled = true;
149 log_reset_gnutls_level();
152 log_error("No such log category: %s", cat);
156 void log_reset_gnutls_level(void) {
159 for (i = ELEMENTSOF(gnutls_log_map) - 1; i >= 0; i--)
160 if (gnutls_log_map[i].enabled) {
161 log_debug("Setting gnutls log level to %d", i);
162 gnutls_global_set_log_level(i);
167 static int verify_cert_authorized(gnutls_session_t session) {
169 gnutls_certificate_type_t type;
173 r = gnutls_certificate_verify_peers2(session, &status);
175 log_error("gnutls_certificate_verify_peers2 failed: %s", strerror(-r));
179 type = gnutls_certificate_type_get(session);
180 r = gnutls_certificate_verification_status_print(status, type, &out, 0);
182 log_error("gnutls_certificate_verification_status_print failed: %s", strerror(-r));
186 log_info("Certificate status: %s", out.data);
188 return status == 0 ? 0 : -EPERM;
191 static int get_client_cert(gnutls_session_t session, gnutls_x509_crt_t *client_cert) {
192 const gnutls_datum_t *pcert;
194 gnutls_x509_crt_t cert;
200 pcert = gnutls_certificate_get_peers(session, &listsize);
201 if (!pcert || !listsize) {
202 log_error("Failed to retrieve certificate chain");
206 r = gnutls_x509_crt_init(&cert);
208 log_error("Failed to initialize client certificate");
212 /* Note that by passing values between 0 and listsize here, you
213 can get access to the CA's certs */
214 r = gnutls_x509_crt_import(cert, &pcert[0], GNUTLS_X509_FMT_DER);
216 log_error("Failed to import client certificate");
217 gnutls_x509_crt_deinit(cert);
225 static int get_auth_dn(gnutls_x509_crt_t client_cert, char **buf) {
230 assert(*buf == NULL);
232 r = gnutls_x509_crt_get_dn(client_cert, NULL, &len);
233 if (r != GNUTLS_E_SHORT_MEMORY_BUFFER) {
234 log_error("gnutls_x509_crt_get_dn failed");
242 gnutls_x509_crt_get_dn(client_cert, *buf, &len);
246 int check_permissions(struct MHD_Connection *connection, int *code, char **hostname) {
247 const union MHD_ConnectionInfo *ci;
248 gnutls_session_t session;
249 gnutls_x509_crt_t client_cert;
250 _cleanup_free_ char *buf = NULL;
258 ci = MHD_get_connection_info(connection,
259 MHD_CONNECTION_INFO_GNUTLS_SESSION);
261 log_error("MHD_get_connection_info failed: session is unencrypted");
262 *code = mhd_respond(connection, MHD_HTTP_FORBIDDEN,
263 "Encrypted connection is required");
266 session = ci->tls_session;
269 r = get_client_cert(session, &client_cert);
271 *code = mhd_respond(connection, MHD_HTTP_UNAUTHORIZED,
272 "Authorization through certificate is required");
276 r = get_auth_dn(client_cert, &buf);
278 *code = mhd_respond(connection, MHD_HTTP_UNAUTHORIZED,
279 "Failed to determine distinguished name from certificate");
283 log_info("Connection from %s", buf);
290 r = verify_cert_authorized(session);
292 log_warning("Client is not authorized");
293 *code = mhd_respond(connection, MHD_HTTP_UNAUTHORIZED,
294 "Client certificate not signed by recognized authority");
300 int check_permissions(struct MHD_Connection *connection, int *code, char **hostname) {