/* * "$Id: tls-gnutls.c 3757 2012-03-30 06:13:47Z msweet $" * * TLS support code for the CUPS scheduler using GNU TLS. * * Copyright 2007-2012 by Apple Inc. * Copyright 1997-2007 by Easy Software Products, all rights reserved. * * These coded instructions, statements, and computer programs are the * property of Apple Inc. and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "LICENSE.txt" * which should have been included with this file. If this file is * file is missing or damaged, see the license at "http://www.cups.org/". * * Contents: * * cupsdEndTLS() - Shutdown a secure session with the client. * cupsdStartTLS() - Start a secure session with the client. * make_certificate() - Make a self-signed SSL/TLS certificate. */ /* * Local functions... */ static int make_certificate(cupsd_client_t *con); /* * 'cupsdEndTLS()' - Shutdown a secure session with the client. */ int /* O - 1 on success, 0 on error */ cupsdEndTLS(cupsd_client_t *con) /* I - Client connection */ { int error; /* Error code */ gnutls_certificate_server_credentials *credentials; /* TLS 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); } /* * 'cupsdStartTLS()' - Start a secure session with the client. */ int /* O - 1 on success, 0 on error */ cupsdStartTLS(cupsd_client_t *con) /* I - Client connection */ { int status; /* Error code */ gnutls_certificate_server_credentials *credentials; /* TLS credentials */ cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Encrypting connection.", con->http.fd); /* * Verify that we have a certificate... */ if (access(ServerKey, 0) || access(ServerCertificate, 0)) { /* * Nope, make a self-signed certificate... */ if (!make_certificate(con)) return (0); } /* * Create the SSL object and perform the SSL handshake... */ 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); } /* * 'make_certificate()' - Make a self-signed SSL/TLS certificate. */ static int /* O - 1 on success, 0 on failure */ make_certificate(cupsd_client_t *con) /* I - Client connection */ { gnutls_x509_crt crt; /* Self-signed certificate */ gnutls_x509_privkey key; /* Encryption key */ cups_lang_t *language; /* Default language info */ cups_file_t *fp; /* Key/cert file */ unsigned char buffer[8192]; /* Buffer for x509 data */ size_t bytes; /* Number of bytes of data */ unsigned char serial[4]; /* Serial number buffer */ time_t curtime; /* Current time */ int result; /* Result of GNU TLS calls */ /* * Create the encryption key... */ cupsdLogMessage(CUPSD_LOG_INFO, "Generating SSL server key..."); gnutls_x509_privkey_init(&key); gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0); /* * Save it... */ 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); } /* * Create the self-signed certificate... */ 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); /* * Save it... */ 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)); /* * Cleanup... */ gnutls_x509_crt_deinit(crt); gnutls_x509_privkey_deinit(key); return (1); } /* * End of "$Id: tls-gnutls.c 3757 2012-03-30 06:13:47Z msweet $". */