471 lines
14 KiB
PHP
471 lines
14 KiB
PHP
<? /*
|
|
Copyright (C) 2004 by Duane Groth <duane_at_CAcert_dot_org>
|
|
|
|
This file is part of CAcert.
|
|
|
|
CAcert has been released under the CAcert Source License
|
|
which can be found included with these source files or can
|
|
be downloaded from the internet from the following address:
|
|
http://www.cacert.org/src-lic.php
|
|
|
|
CAcert is distributed WITHOUT ANY WARRANTY; without even
|
|
the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
PARTICULAR PURPOSE. See the License for more details.
|
|
*/ ?>
|
|
<pre> 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 <CRLF> (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 "?<CRLF>" (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);
|
|
}</pre>
|