static int make_certificate(cupsd_client_t *con);
int
cupsdEndTLS(cupsd_client_t *con)
{
int error;
gnutls_certificate_server_credentials *credentials;
credentials = (gnutls_certificate_server_credentials *)
(con->http.tls_credentials);
error = gnutls_bye(con->http.tls, GNUTLS_SHUT_WR);
switch (error)
{
case GNUTLS_E_SUCCESS:
cupsdLogMessage(CUPSD_LOG_DEBUG,
"SSL shutdown successful!");
break;
default:
cupsdLogMessage(CUPSD_LOG_ERROR,
"SSL shutdown failed: %s", gnutls_strerror(error));
break;
}
gnutls_deinit(con->http.tls);
con->http.tls = NULL;
gnutls_certificate_free_credentials(*credentials);
free(credentials);
return (1);
}
int
cupsdStartTLS(cupsd_client_t *con)
{
int status;
gnutls_certificate_server_credentials *credentials;
cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Encrypting connection.",
con->http.fd);
if (access(ServerKey, 0) || access(ServerCertificate, 0))
{
if (!make_certificate(con))
return (0);
}
credentials = (gnutls_certificate_server_credentials *)
malloc(sizeof(gnutls_certificate_server_credentials));
if (credentials == NULL)
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Unable to encrypt connection from %s - %s",
con->http.hostname, strerror(errno));
return (0);
}
gnutls_certificate_allocate_credentials(credentials);
gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate,
ServerKey, GNUTLS_X509_FMT_PEM);
gnutls_init(&con->http.tls, GNUTLS_SERVER);
gnutls_set_default_priority(con->http.tls);
gnutls_credentials_set(con->http.tls, GNUTLS_CRD_CERTIFICATE, *credentials);
gnutls_transport_set_ptr(con->http.tls, (gnutls_transport_ptr)HTTP(con));
gnutls_transport_set_pull_function(con->http.tls, _httpReadGNUTLS);
gnutls_transport_set_push_function(con->http.tls, _httpWriteGNUTLS);
while ((status = gnutls_handshake(con->http.tls)) != GNUTLS_E_SUCCESS)
{
if (gnutls_error_is_fatal(status))
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Unable to encrypt connection from %s - %s",
con->http.hostname, gnutls_strerror(status));
gnutls_deinit(con->http.tls);
gnutls_certificate_free_credentials(*credentials);
con->http.tls = NULL;
free(credentials);
return (0);
}
}
cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
con->http.hostname);
con->http.tls_credentials = credentials;
return (1);
}
static int
make_certificate(cupsd_client_t *con)
{
gnutls_x509_crt crt;
gnutls_x509_privkey key;
cups_lang_t *language;
cups_file_t *fp;
unsigned char buffer[8192];
size_t bytes;
unsigned char serial[4];
time_t curtime;
int result;
cupsdLogMessage(CUPSD_LOG_INFO, "Generating SSL server key...");
gnutls_x509_privkey_init(&key);
gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0);
bytes = sizeof(buffer);
if ((result = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM,
buffer, &bytes)) < 0)
{
cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to export SSL server key - %s",
gnutls_strerror(result));
gnutls_x509_privkey_deinit(key);
return (0);
}
else if ((fp = cupsFileOpen(ServerKey, "w")) != NULL)
{
cupsFileWrite(fp, (char *)buffer, bytes);
cupsFileClose(fp);
cupsdLogMessage(CUPSD_LOG_INFO, "Created SSL server key file \"%s\"...",
ServerKey);
}
else
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Unable to create SSL server key file \"%s\" - %s",
ServerKey, strerror(errno));
gnutls_x509_privkey_deinit(key);
return (0);
}
cupsdLogMessage(CUPSD_LOG_INFO, "Generating self-signed SSL certificate...");
language = cupsLangDefault();
curtime = time(NULL);
serial[0] = curtime >> 24;
serial[1] = curtime >> 16;
serial[2] = curtime >> 8;
serial[3] = curtime;
gnutls_x509_crt_init(&crt);
if (strlen(language->language) == 5)
gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
language->language + 3, 2);
else
gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
"US", 2);
gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0,
ServerName, strlen(ServerName));
gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATION_NAME, 0,
ServerName, strlen(ServerName));
gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME,
0, "Unknown", 7);
gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0,
"Unknown", 7);
gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_LOCALITY_NAME, 0,
"Unknown", 7);
gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_PKCS9_EMAIL, 0,
ServerAdmin, strlen(ServerAdmin));
gnutls_x509_crt_set_key(crt, key);
gnutls_x509_crt_set_serial(crt, serial, sizeof(serial));
gnutls_x509_crt_set_activation_time(crt, curtime);
gnutls_x509_crt_set_expiration_time(crt, curtime + 10 * 365 * 86400);
gnutls_x509_crt_set_ca_status(crt, 0);
gnutls_x509_crt_set_subject_alternative_name(crt, GNUTLS_SAN_DNSNAME,
ServerName);
gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_SERVER, 0);
gnutls_x509_crt_set_key_usage(crt, GNUTLS_KEY_KEY_ENCIPHERMENT);
gnutls_x509_crt_set_version(crt, 3);
bytes = sizeof(buffer);
if (gnutls_x509_crt_get_key_id(crt, 0, buffer, &bytes) >= 0)
gnutls_x509_crt_set_subject_key_id(crt, buffer, bytes);
gnutls_x509_crt_sign(crt, crt, key);
bytes = sizeof(buffer);
if ((result = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM,
buffer, &bytes)) < 0)
cupsdLogMessage(CUPSD_LOG_ERROR,
"Unable to export SSL server certificate - %s",
gnutls_strerror(result));
else if ((fp = cupsFileOpen(ServerCertificate, "w")) != NULL)
{
cupsFileWrite(fp, (char *)buffer, bytes);
cupsFileClose(fp);
cupsdLogMessage(CUPSD_LOG_INFO,
"Created SSL server certificate file \"%s\"...",
ServerCertificate);
}
else
cupsdLogMessage(CUPSD_LOG_ERROR,
"Unable to create SSL server certificate file \"%s\" - %s",
ServerCertificate, strerror(errno));
gnutls_x509_crt_deinit(crt);
gnutls_x509_privkey_deinit(key);
return (1);
}