/* LibreSSL - CAcert web application Copyright (C) 2004-2008 CAcert Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ ?>
CERTIFICATE RETRIEVAL STATUS OF THIS MEMO This document is an Internet-Draft and is subject to all provisions of Section 10 of RFC2026.. INTRODUCTION The CERTIFICATE RETRIEVAL Service is a TCP transaction based query/response server, running on port xxxxx, that provides directory service for internet users to automate the task of retrieving certificates based on email addresses or hostnames. This service, together with a corresponding database of X.509 certificates can be used to automate security in conjunction with the 802.1x protocol for authentication and encryption, webmail services, and email in general that employ s/mime to encrypt replies to authors could request a certificate via the service rather then requiring the user to supply it up front, so as long as you know the email address and they have signed up for a certificate the message can be encrypted. This method of distributing client certificates isn't intended for extremely sensitive material, but merely to protect day to day emails that are currently being sent as clear text. The security risk in this method isn't more likely to succeed then signing an email and distributing the certificate in that manner. PROTOCOL To access the CERTIFICATE RETRIEVAL service: Connect to the service host on TCP service port xxxxx (decimal). Send a single "command line", ending with(ASCII CR and LF). Receive information in response to the command line. The server closes its connection as soon as the output is finished. Extention to Service: This service could be extended to relay services that worked inline with Certificate Authorities to cache responses and verify the certificates were still valid using OCSP services. Using the relay method it would be easier to include additional Certificate Authorities, instead of issuing client software updates every time a new Certificate Authority commenced operations, or alternatively ceased to operate. Due to political concerns more then technical issues relay services should be run by impartial parties that don't have a vested interest in competing with other Certificate Authorities so no user would be disadvantaged. SECURITY ISSUES Spam: Obviously this would open a potential method for spammers to validate their spam lists, and sending encrypted spam which may bypass current spam filters. This system to an extent mimics the PGP Key Exchange service, unlike the PGP Key Exchange there will be no effort to allow searching apart from exact email or hostnames. At time of writting there are no documented attempts to exploit the PGP Key Exchange for the purposes of spam. Certificate Authorities should offer a way for their users to opt out of this method of distributing public certificates. Transfer: Due to the nature of X.509 it would be highly improbable that the certificates could be compromised if they passed via a compromised relay, or as the information is passed via any communications medium. After the certificate is received issuer, CRL and OCSP checks should be performed by client software as per normal with any certificate received by other methods. SIMILAR SERVICES LDAP: LDAP can already be used in a method similar to described in this document, however a simpler approach to security through an extremely simple, efficient method of distributing without the need for bulky libs and wrappers would help adoption and help increase security in general. PGP Key Exchange: Has much more extended capabilities although the concept is basically the same, an easy method of distributing public keys. OCSP/CRL: Much simpler services only responding to requests about validity of a certificate presented to them, will not respond with a copy of the certificate. COMMAND LINES AND REPLIES A command line is normally a single name specification. Note that the specification formats will evolve with time; the best way to obtain the most recent documentation on name specifications is to give the server a command line consisting of "? " (that is, a question-mark alone as the name specification). The response from the server will list all possible formats that can be used. The responses are currently intended to be machine-readable; the information is not meant to be passed back directly to a human user. The following three examples illustrate the use of the service as of February 2004. --------------------------------------------------------------------- Command line: ? Response: Please enter an email address or hostname, such as "www.cacert.org". --------------------------------------------------------------------- Command line: myisp.com Response: No valid certificate available. --------------------------------------------------------------------- Command line: www.cacert.org Response: -----BEGIN CERTIFICATE----- MIIFiTCCA3GgAwIBAgICAjYwDQYJKoZIhvcNAQEEBQAweTEQMA4GA1UEChMHUm9v dCBDQTEeMBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlD QSBDZXJ0IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0 QGNhY2VydC5vcmcwHhcNMDMwNDAyMDAzNzU2WhcNMDUwNDAxMDAzNzU2WjCBmjEL MAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEPMA0GA1UEBxMGU3lkbmV5MRAwDgYD VQQKEwdDQSBDZXJ0MR4wHAYDVQQLExVTZXJ2ZXIgQWRtaW5pc3RyYXRpb24xFzAV BgNVBAMTDnd3dy5jYWNlcnQub3JnMSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNh Y2VydC5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMWKlVe5/cfDZiPq WZTUGvgLA4dbvj/cBJXZgkz6aIvKuBhZ+cXob/xc6tkL20NdXZIW6WIzaGk6SU0a vu6grLlHumwuXrFGPNRb8HyRQrvWzgD73Est+CDfLo+Omq55UjDDH0TZoAMV3L0N Gb/S7YZeYHNcUzAHcXTE1//LyS8bAgMBAAGjggF7MIIBdzAMBgNVHRMBAf8EAjAA MCoGA1UdJQQjMCEGCCsGAQUFBwMBBglghkgBhvhCBAEGCisGAQQBgjcKAwMwCwYD VR0PBAQDAgUgMB0GA1UdDgQWBBTN0mx8ONpAgO9SaMf5KcKmDjFgDjCBowYDVR0j BIGbMIGYgBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9v dCBDQTEeMBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlD QSBDZXJ0IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0 QGNhY2VydC5vcmeCAQAwEQYJYIZIAYb4QgEBBAQDAgZAMFYGCWCGSAGG+EIBDQRJ FkdUbyBnZXQgeW91ciBvd24gY2VydGlmaWNhdGUgZm9yIEZSRUUgaGVhZCBvdmVy IHRvIGh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzANBgkqhkiG9w0BAQQFAAOCAgEAc7vb l/zrCweRmHo8dQw7fpvX4KibbpZ5fXT9c1tn+oIUxV1erDI3r5YW6Cha8XIDhMxJ tKPXdyAkrzEt3SrQrFy/KndMCjS1ypic2g/IhUjnB7FnH7Jc3RF+w3GX6OO38XtQ ydcQk2qD125W7Kl6cgSXjauVz2AyjetHi0jA+SRcmnlIRafKLEDPsnc/1A6CFd4C e8J+mrSA4FPOwf1ezAEJAlsBNiM2oIcfbWjrUR/dfaBrGweBQS/mgXxSXJWJnFQI wNE47X3ibISAgT74p/zqdVoYBQEXe0LDNsmFU6eqc8OyLEKW3BIgfQxtGxKCLCsA G/TjfjxGRhi1FlX9ZPvuWGfV0ks0gMa/VDM7V37LMhVViyTHNTmKuHsOa7UAz/5x qJo/ruLuCfGF9Z8jfj0197J0LMQSr09YQ/JSDJE6IFE59/6ju7+Py+2NDRsiqo+/ qi/4SGbXakOXqJv92NHLqD/jdq8D8RIPggQ2RGG7aNQ5bpmnZk6KEJH9jD50LAs1 kU+KI/v8o94ZBz+MDhNgJpwT5R/Vvnri2iT9mIysfpCuZCZC6KJKN8rXT031ocmx CJyaTBopnpnHwGu238IAMSpq9nPibhCb0OlqMVHdid9qPr8iZDWVhgmgDasigS5Y lzsrd9lRVDVN5HITUCFG3Qr5xx7ltSouj/dkykg= -----END CERTIFICATE----- --------------------------------------------------------------------- EXAMPLE CODE --------------------------------------------------------------------- /* Copyright Jeremey Barrett 2004 for CAcert.org You may create derivative works, as long as this copyright notice remains at the top */ /* Example finger daemon that can return certificates from a database. Currently only tested on debian stable. use the following command to build program gcc -o cacert-finger cacert-finger.c -I/usr/include/mysql -L/usr/lib \ -lmysqlclient -lz -DUSE_MYSQL */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/select.h> #include <netinet/in.h> #ifdef USE_MYSQL #include <mysql.h> #endif #ifdef USE_MYSQL static MYSQL mysql_db; #endif #define DEFAULT_PORT 79 #define EMAIL_QUERY "select CRT from certs where `EMAIL`='%s' and \ `revoked`='0000-00-00 00:00:00'" #define DOMAIN_QUERY "select CRT from domaincerts where \ `revoked`='0000-00-00 00:00:00' and `CN`='%s'" #define WILD_DOMAIN_QUERY "select CRT from domaincerts where \ `revoked`='0000-00-00 00:00:00' and (`CN`='%s' OR `CN`='%s')" void err_exit(void) { perror(""); exit(1); } #ifdef USE_MYSQL void do_mysql(int client_fd, char *query) { MYSQL_RES *res; MYSQL_ROW row; char *cert; if(!(mysql_real_connect(&mysql_db, "host", "username", "password", "database", 0, "/var/run/mysqld/mysqld.sock", 0) != NULL)) { printf(mysql_error(&mysql_db)); exit(1); } if(mysql_real_query(&mysql_db, query, strlen(query))) goto _err_return; res = mysql_store_result(&mysql_db); if(mysql_num_rows(res) > 0) { row = mysql_fetch_row(res); if(!row[0]) goto _free_res_return; cert = strstr(row[0], "-----BEGIN CERTIFICATE"); if(cert) { char *response; int response_len = strlen(cert)+2; response = (char *)calloc(1, response_len+1); if(!response) goto _free_res_return; memcpy(response, cert, response_len-2); response[response_len-2] = '\r'; response[response_len-1] = '\n'; /* should be checked for errors, more writing */ write(client_fd, response, response_len); free(response); } } _free_res_return: mysql_free_result(res); _err_return: mysql_close(&mysql_db); } #endif int read_line(int fd, char *buf, int size) { int n = 0; int t = 0; char *p; char *q; for(; t < size; ) { n = read(fd, buf+t, size-t); if(n < 0) { switch(errno) { case EINTR: continue; default: return -1; } } if(n == 0) { return t; } if(t) p = buf+t-1; else p = buf; for(; *p && *p != '\n' && *p != '\r'; p++); if(*p) { *p = 0; t = p-buf; return t; } t += n; } return -1; } int handle_request(int fd) { char *buf; int buf_size; int len; int i; int email_query = 0; char query[4096]; /* ugly */ char *email = NULL; char *domain = NULL; char *wildcard = NULL; buf_size = 2048; buf = (char *)calloc(1, buf_size); if(!buf) return -1; if((len = read_line(fd, buf, buf_size-1)) <= 0) goto _free_return; memset(buf+len, 0, buf_size-len); if(strchr(buf, '@')) { email_query = 1; email = buf; } else { char *p; char *q; p = strchr(buf, '.'); if(p) { q = strchr(p+1, '.'); if(q) { wildcard = (char *)calloc(1, strlen(p)+2); if(wildcard) { *wildcard = '*'; memcpy(wildcard+1, p, strlen(p)); } } } domain = buf; } memset(query, 0, sizeof(query)); if(email_query) { snprintf(query, sizeof(query)-2, EMAIL_QUERY, email); } else { if(wildcard && strcmp(wildcard, domain)) snprintf(query, sizeof(query)-2, WILD_DOMAIN_QUERY, domain, wildcard); else snprintf(query, sizeof(query)-2, DOMAIN_QUERY, domain); if(wildcard) free(wildcard); } /* go and do MySQL stuff here */ #ifdef USE_MYSQL do_mysql(fd, query); #else strcat(query, "\n"); write(fd, query, strlen(query)); #endif _free_return: free(buf); return 0; } int main(int argc, char **argv) { struct sockaddr_in server_sock; struct sockaddr_in client_sock; int client_addr_size; int server_fd = -1; int client_fd = -1; int pid; /* take command line args, e.g. -p <port>, later */ if ((pid=fork()) < 0) { perror ("Fork failed"); exit(errno); } if (pid) { exit(0); } server_fd = socket(PF_INET, SOCK_STREAM, 0); if(server_fd < 0) err_exit(); memset(&server_sock, 0, sizeof(server_sock)); server_sock.sin_family = AF_INET; server_sock.sin_addr.s_addr = INADDR_ANY; server_sock.sin_port = htons(DEFAULT_PORT); if(bind(server_fd, (struct sockaddr *)&server_sock, sizeof(struct sockaddr_in)) < 0) err_exit(); if(listen(server_fd, 64) < 0) err_exit(); setgid(65534); setuid(65534); for(;;) { memset(&client_sock, 0, sizeof(client_sock)); client_addr_size = sizeof(client_sock); client_fd = accept(server_fd, (struct sockaddr *)&client_sock, &client_addr_size); if(client_fd < 0) err_exit(); if(handle_request(client_fd) < 0) break; close(client_fd); client_fd = -1; } if(client_fd >= 0) close(client_fd); close(server_fd); exit(0); }