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"
33 #include <gnutls/gnutls.h>
34 #include <gnutls/x509.h>
37 void microhttpd_logger(void *arg, const char *fmt, va_list ap) {
38 _cleanup_free_ char *f = NULL;
40 if (asprintf(&f, "microhttpd: %s", fmt) <= 0) {
45 DISABLE_WARNING_FORMAT_NONLITERAL;
46 log_metav(LOG_INFO, NULL, 0, NULL, f, ap);
51 static int mhd_respond_internal(struct MHD_Connection *connection,
52 enum MHD_RequestTerminationCode code,
55 enum MHD_ResponseMemoryMode mode) {
56 struct MHD_Response *response;
61 response = MHD_create_response_from_buffer(size, buffer, mode);
65 log_debug("Queing response %u: %s", code, buffer);
66 MHD_add_response_header(response, "Content-Type", "text/plain");
67 r = MHD_queue_response(connection, code, response);
68 MHD_destroy_response(response);
73 int mhd_respond(struct MHD_Connection *connection,
74 enum MHD_RequestTerminationCode code,
75 const char *message) {
77 return mhd_respond_internal(connection, code,
78 (char*) message, strlen(message),
79 MHD_RESPMEM_PERSISTENT);
82 int mhd_respond_oom(struct MHD_Connection *connection) {
83 return mhd_respond(connection, MHD_HTTP_SERVICE_UNAVAILABLE, "Out of memory.\n");
86 int mhd_respondf(struct MHD_Connection *connection,
87 enum MHD_RequestTerminationCode code,
88 const char *format, ...) {
98 r = vasprintf(&m, format, ap);
102 return respond_oom(connection);
104 r = mhd_respond_internal(connection, code, m, r, MHD_RESPMEM_MUST_FREE);
112 static int log_level_map[] = {
114 LOG_WARNING, /* gnutls session audit */
115 LOG_DEBUG, /* gnutls debug log */
116 LOG_WARNING, /* gnutls assert log */
117 LOG_INFO, /* gnutls handshake log */
118 LOG_DEBUG, /* gnutls record log */
119 LOG_DEBUG, /* gnutls dtls log */
123 LOG_DEBUG, /* gnutls hard log */
124 LOG_DEBUG, /* gnutls read log */
125 LOG_DEBUG, /* gnutls write log */
126 LOG_DEBUG, /* gnutls io log */
127 LOG_DEBUG, /* gnutls buffers log */
130 void log_func_gnutls(int level, const char *message) {
135 if (0 <= level && level < (int) ELEMENTSOF(log_level_map))
136 ourlevel = log_level_map[level];
140 log_meta(ourlevel, NULL, 0, NULL, "gnutls: %s", message);
143 static int verify_cert_authorized(gnutls_session_t session) {
145 gnutls_certificate_type_t type;
149 r = gnutls_certificate_verify_peers2(session, &status);
151 log_error("gnutls_certificate_verify_peers2 failed: %s", strerror(-r));
155 type = gnutls_certificate_type_get(session);
156 r = gnutls_certificate_verification_status_print(status, type, &out, 0);
158 log_error("gnutls_certificate_verification_status_print failed: %s", strerror(-r));
162 log_info("Certificate status: %s", out.data);
164 return status == 0 ? 0 : -EPERM;
167 static int get_client_cert(gnutls_session_t session, gnutls_x509_crt_t *client_cert) {
168 const gnutls_datum_t *pcert;
170 gnutls_x509_crt_t cert;
176 pcert = gnutls_certificate_get_peers(session, &listsize);
177 if (!pcert || !listsize) {
178 log_error("Failed to retrieve certificate chain");
182 r = gnutls_x509_crt_init(&cert);
184 log_error("Failed to initialize client certificate");
188 /* Note that by passing values between 0 and listsize here, you
189 can get access to the CA's certs */
190 r = gnutls_x509_crt_import(cert, &pcert[0], GNUTLS_X509_FMT_DER);
192 log_error("Failed to import client certificate");
193 gnutls_x509_crt_deinit(cert);
201 static int get_auth_dn(gnutls_x509_crt_t client_cert, char **buf) {
206 assert(*buf == NULL);
208 r = gnutls_x509_crt_get_dn(client_cert, NULL, &len);
209 if (r != GNUTLS_E_SHORT_MEMORY_BUFFER) {
210 log_error("gnutls_x509_crt_get_dn failed");
218 gnutls_x509_crt_get_dn(client_cert, *buf, &len);
222 int check_permissions(struct MHD_Connection *connection, int *code) {
223 const union MHD_ConnectionInfo *ci;
224 gnutls_session_t session;
225 gnutls_x509_crt_t client_cert;
226 char _cleanup_free_ *buf = NULL;
234 ci = MHD_get_connection_info(connection,
235 MHD_CONNECTION_INFO_GNUTLS_SESSION);
237 log_error("MHD_get_connection_info failed: session is unencrypted");
238 *code = mhd_respond(connection, MHD_HTTP_FORBIDDEN,
239 "Encrypted connection is required");
242 session = ci->tls_session;
245 r = get_client_cert(session, &client_cert);
247 *code = mhd_respond(connection, MHD_HTTP_UNAUTHORIZED,
248 "Authorization through certificate is required");
252 r = get_auth_dn(client_cert, &buf);
254 *code = mhd_respond(connection, MHD_HTTP_UNAUTHORIZED,
255 "Failed to determine distinguished name from certificate");
259 log_info("Connection from DN %s", buf);
261 r = verify_cert_authorized(session);
263 log_warning("Client is not authorized");
264 *code = mhd_respond(connection, MHD_HTTP_UNAUTHORIZED,
265 "Client certificate not signed by recognized authority");
271 int check_permissions(struct MHD_Connection *connection, int *code) {