/* * Copyright (C) 2013 Nikos Mavrogiannopoulos * * 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; either version 2 of the License, or * (at your option) any later version. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_PIN_SIZE GNUTLS_PKCS11_MAX_PIN_LEN struct pin_st { char pin[MAX_PIN_SIZE]; char srk_pin[MAX_PIN_SIZE]; }; static int pin_callback (void *user, int attempt, const char *token_url, const char *token_label, unsigned int flags, char *pin, size_t pin_max) { struct pin_st * ps = user; int srk = 0; const char* p; unsigned len; if (flags & GNUTLS_PIN_FINAL_TRY) { syslog(LOG_ERR, "PIN callback: final try before locking; not attempting to unlock"); return -1; } if (flags & GNUTLS_PIN_WRONG) { syslog(LOG_ERR, "PIN callback: wrong PIN was entered for '%s' (%s)", token_label, token_url); return -1; } if (ps->pin[0] == 0) { syslog(LOG_ERR, "PIN required for '%s' but pin-file was not set", token_label); return -1; } if (strcmp(token_url, "SRK") == 0 || strcmp(token_label, "SRK") == 0) { srk = 1; p = ps->srk_pin; } else { p = ps->pin; } if (srk != 0 && ps->srk_pin[0] == 0) { syslog(LOG_ERR, "PIN required for '%s' but srk-pin-file was not set", token_label); return -1; } len = strlen(p); if (len > pin_max-1) { syslog(LOG_ERR, "Too long PIN (%u chars)", len); return -1; } memcpy(pin, p, len); pin[len] = 0; return 0; } static int load_pins(struct cfg_st* config, struct pin_st* s) { int fd, ret; s->srk_pin[0] = 0; s->pin[0] = 0; if (config->srk_pin_file != NULL) { fd = open(config->srk_pin_file, O_RDONLY); if (fd < 0) { syslog(LOG_ERR, "could not open SRK PIN file '%s'", config->srk_pin_file); return -1; } ret = read(fd, s->srk_pin, sizeof(s->srk_pin)); close(fd); if (ret <= 1) { syslog(LOG_ERR, "could not read from PIN file '%s'", config->srk_pin_file); return -1; } if (s->srk_pin[ret-1] == '\n' || s->srk_pin[ret-1] == '\r') s->srk_pin[ret-1] = 0; s->srk_pin[ret] = 0; } if (config->pin_file != NULL) { fd = open(config->pin_file, O_RDONLY); if (fd < 0) { syslog(LOG_ERR, "could not open PIN file '%s'", config->pin_file); return -1; } ret = read(fd, s->pin, sizeof(s->pin)); close(fd); if (ret <= 1) { syslog(LOG_ERR, "could not read from PIN file '%s'", config->pin_file); return -1; } if (s->pin[ret-1] == '\n' || s->pin[ret-1] == '\r') s->pin[ret-1] = 0; s->pin[ret] = 0; } return 0; } void sec_mod_server(struct cfg_st* config, int sd) { struct sockaddr_un sa; socklen_t sa_len; int cfd, ret, e; unsigned i, buffer_size, type; gnutls_privkey_t *key; uint8_t *buffer; unsigned key_size = config->key_size; struct pin_st pins; gnutls_datum_t data, out; uint16_t length; signal(SIGHUP, SIG_IGN); signal(SIGINT, SIG_DFL); signal(SIGTERM, SIG_DFL); buffer_size = 8*1024; buffer = malloc(buffer_size); if (buffer == NULL) { syslog(LOG_ERR, "error in memory allocation"); exit(1); } ret = load_pins(config, &pins); if (ret < 0) { syslog(LOG_ERR, "error loading PIN files"); exit(1); } key = malloc(sizeof(*key)*config->key_size); if (key == NULL) { syslog(LOG_ERR, "error in memory allocation"); exit(1); } /* read private keys */ for (i=0;ikey[i]) != 0) { gnutls_privkey_set_pin_function (key[i], pin_callback, &pins); ret = gnutls_privkey_import_url(key[i], config->key[i], 0); GNUTLS_FATAL_ERR(ret); } else { ret = gnutls_load_file(config->key[i], &data); if (ret < 0) { syslog(LOG_ERR, "error loading file '%s'", config->key[i]); GNUTLS_FATAL_ERR(ret); } ret = gnutls_privkey_import_x509_raw(key[i], &data, GNUTLS_X509_FMT_PEM, NULL, 0); GNUTLS_FATAL_ERR(ret); gnutls_free(data.data); } } for (;;) { sa_len = sizeof(sa); cfd = accept(sd, (struct sockaddr *)&sa, &sa_len); if (cfd == -1) { e = errno; syslog(LOG_ERR, "error accepting sec-mod connection: %s", strerror(e)); continue; } /* read request */ ret = recv(cfd, buffer, buffer_size, 0); if (ret <= 2) { e = errno; syslog(LOG_ERR, "error receiving sec-mod data: %s", strerror(e)); goto cont; } /* calculate */ i = buffer[0]; type = buffer[1]; if (i >= key_size) { syslog(LOG_ERR, "sec-mod received out-of-bounds key index"); goto cont; } data.data = &buffer[2]; data.size = ret - 2; #if GNUTLS_VERSION_NUMBER >= 0x03010a if (type == 'S') { ret = gnutls_privkey_sign_raw_data(key[i], 0, &data, &out); } else #endif if (type == 'D') { ret = gnutls_privkey_decrypt_data(key[i], 0, &data, &out); } else { syslog(LOG_ERR, "unknown type 0x%.2x", type); goto cont; } if (ret < 0) { syslog(LOG_ERR, "sec-mod error in crypto operation: %s", gnutls_strerror(ret)); goto cont; } /* write reply */ length = out.size; force_write(cfd, &length, 2); force_write(cfd, out.data, out.size); gnutls_free(out.data); cont: close(cfd); } }