Wrong licensing texts removed and GPL code eliminated.
[igd2-for-linux:jeevans-deviceprotection.git] / pupnp_branch-1.6.x / upnp / src / genlib / util / pki.c
1 ///////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2009-2011  Nokia Corporation and/or its subsidiary(-ies).
4 // All rights reserved. 
5 //
6 // Contact: mika.saaranen@nokia.com
7 // Developer(s): jaakko.pasanen@tieto.com, opensource@tieto.com
8 //
9 // Redistribution and use in source and binary forms, with or without 
10 // modification, are permitted provided that the following conditions are met: 
11 //
12 // * Redistributions of source code must retain the above copyright notice, 
13 // this list of conditions and the following disclaimer. 
14 // * Redistributions in binary form must reproduce the above copyright notice, 
15 // this list of conditions and the following disclaimer in the documentation 
16 // and/or other materials provided with the distribution. 
17 // * Neither name of Nokia Corporation nor the names of its contributors 
18 // may be used to endorse or promote products derived from this software 
19 // without specific prior written permission.
20 // 
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NOKIA OR 
25 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
26 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
27 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
28 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
29 // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
31 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 ///////////////////////////////////////////////////////////////////////////
34
35 /************************************************************************
36 * Purpose: This file contains functions that operate on X.509 Public-Key 
37 * Infrastructure.
38 * Certificate and private key creation and such. 
39 ************************************************************************/
40
41 #include <sys/stat.h>
42
43 #include <gnutls/gnutls.h>
44 #include <gnutls/x509.h>
45 #include <gcrypt.h>
46
47 #include "upnpapi.h"
48 #include "pki.h"
49
50 /* Make libgrypt (gnutls) thread save. This assumes that we are using pthred for threading.
51  * Check http://www.gnu.org/software/gnutls/manual/gnutls.html#Multi_002dthreaded-applications
52  * Also see StartHttpsServer
53  */
54 GCRY_THREAD_OPTION_PTHREAD_IMPL;
55
56
57 // Local Certificate Authority certificate its and private key
58 static gnutls_x509_crt_t ca_crt = NULL;
59 static gnutls_x509_privkey_t ca_privkey = NULL;
60
61 /************************************************************************
62 *   Function :  initialize_gcrypt
63 *
64 *   Description :   Initialize libgcrypt for gnutls.
65 *
66 *   Return : int ;
67 *       0 on succes, -1 on error
68 *
69 *   Note : assumes that libupnp uses pthreads.
70 ************************************************************************/
71 static int initialize_gcrypt()
72 {
73     if (!gcry_control (GCRYCTL_INITIALIZATION_FINISHED_P))
74     {
75         fputs ("libgcrypt has not been initialized. pupnp tries to initialize it.\n", stderr);
76
77         /* Version check should be the very first call because it
78           makes sure that important subsystems are intialized. */
79         if (!gcry_check_version (GCRYPT_VERSION))
80         {
81             return -1;
82         }
83
84         /* Make libgrypt (gnutls) thread save. This assumes that we are using pthred for threading.
85            Check http://www.gnu.org/software/gnutls/manual/gnutls.html#Multi_002dthreaded-applications */
86         gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
87
88         /* to disallow usage of the blocking /dev/random  */
89         gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
90
91         /* Disable secure memory.  */
92         gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
93
94         /* Tell Libgcrypt that initialization has completed. */
95         gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
96     }
97     return 0;
98 }
99
100 /************************************************************************
101 *   Function :  init_crypto_libraries
102 *
103 *   Description :   Initialize libgcrypt for gnutls. Not sure should this rather 
104 *        be done in final program using this UPnP library?
105 *        Makes gcrypt thread save, and disables usage of blocking /dev/random.
106 *        Initialize also gnutls.
107 *
108 *   Return : int ;
109 *       0 on succes, gnutls error else
110 *
111 *   Note : assumes that libupnp uses pthreads.
112 ************************************************************************/
113 int init_crypto_libraries()
114 {
115     int ret;
116
117     ret = initialize_gcrypt();
118     if ( ret != 0 ) {
119         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
120             "Failed to initialize libgcrypt \n\n");
121         return ret;
122     }
123
124     /* this must be called once in the program */
125     ret = gnutls_global_init ();
126     if ( ret != GNUTLS_E_SUCCESS ) {
127         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
128             "Failed to initialize gnutls. (%s) \n\n", gnutls_strerror(ret) );
129         return ret;
130     }
131
132     return 0;
133 }
134
135
136 /************************************************************************
137 *   Function :  clientCertCallback
138 *
139 *   Description :   Callback function which is called by gnutls when 
140 *         server asks client certificate at the tls handshake.
141 *         Function sets certificate and private key used by client for 
142 *         response.
143 *
144 *   Return : int
145 *
146 *   Note : Don't call this.
147 ************************************************************************/
148 int clientCertCallback(gnutls_session_t session, const gnutls_datum_t* req_ca_dn, int nreqs, gnutls_pk_algorithm_t* pk_algos, int pk_algos_length, gnutls_retr_st* st)
149 {
150     gnutls_certificate_type type;
151
152     type = gnutls_certificate_type_get(session);
153     if (type == GNUTLS_CRT_X509) {
154         st->type = type;
155         st->ncerts = client_crt_size;
156         st->cert.x509 = client_crt;  // these two are globals defined in upnpapi
157         st->key.x509 = client_privkey;//
158         st->deinit_all = 0;
159     }
160     else {
161         return -1;
162     }
163
164     return 0;
165 }
166
167
168 /************************************************************************
169 *   Function :  read_binary_file
170 *
171 *   Parameters :
172 *       IN const char* filename ;    Name of the file to read
173 *       OUT size_t length       ;    Length of read data 
174 *
175 *   Description :   Read file contents and return contents as string.
176 *                   Size of content is returned in second function parameter.
177 *
178 *   Return : char* ;
179 *       Pointer to the string containing file contents.
180 *       NULL if failed to read file.
181 *
182 *   Note :
183 ************************************************************************/
184 static char* read_binary_file(const char *filename, size_t * length)
185 {
186     FILE *file;
187     char *buffer;
188     unsigned long fileLen;
189
190     //Open file
191     file = fopen(filename, "rb");
192     if (!file) {
193             return NULL;
194     }
195         
196     //Get file length
197     fseek(file, 0, SEEK_END);
198     fileLen = ftell(file);
199     fseek(file, 0, SEEK_SET);
200
201     //Allocate memory
202     buffer = (char *)malloc(fileLen+1);
203     if (!buffer) {
204             return NULL;
205     }
206     
207     fread(buffer, fileLen, 1, file);
208     fclose(file);
209
210     *length = fileLen;
211     return buffer;
212 }
213
214
215 /************************************************************************
216 *   Function :  read_pem_data_file
217 *
218 *   Parameters :
219 *       IN const char* filename ;    Name of the file to read
220 *       OUT gnutls_datum_t *pem_data  ;    Pointer to struct where read data is inserted 
221 *
222 *   Description :   Read file contents and return contents in gnutls_datum_t
223 *       struct.
224 *
225 *   Return : int ;
226 *       0 if all well, -1 if failure.
227 *
228 *   Note :
229 ************************************************************************/
230 static int read_pem_data_file(const char *filename, gnutls_datum_t *pem_data)
231 {
232     size_t size = 0;
233     char *data = read_binary_file(filename,&size);
234
235     if (data && size > 0) {
236         pem_data->data = (unsigned char *)data;
237         pem_data->size = (unsigned int)size;
238     }
239     else {
240         return -1;
241     }
242
243     return 0;
244 }
245
246
247 /************************************************************************
248 *   Function :  export_certificate_to_file
249 *
250 *   Parameters :
251 *       IN const gnutls_x509_crt_t *crt     ;  Pointer to gnutls_x509_crt_t where certificate is created
252 *       IN const gnutls_x509_privkey_t *key ;  Pointer to gnutls_x509_privkey_t where private key is created
253 *       IN const char *certfile        ;  Name of file where certificate is exported in PEM format
254 *       IN const char *privkeyfile     ;  Name of file where private key is exported in PEM format
255         IN const int append_CA    ;  Is CA certificate appended to the end of the certificate file
256 *
257 *   Description :   Export certificate and private key into file(s). Filenames may be same.
258 *
259 *   Return : int ;
260 *       UPNP or gnutls error code.
261 *
262 *   Note :
263 ************************************************************************/
264 static int export_certificate_to_file(const gnutls_x509_crt_t *crt, const gnutls_x509_privkey_t *key, const char *certfile, const char *privkeyfile, int append_CA)
265 {
266     unsigned char buffer[10 * 1024];
267     size_t buffer_size = sizeof (buffer);
268     size_t orig_size = buffer_size;
269     FILE *fp;
270     int ret;
271
272     // certificate and privatekey files must be different files
273     if (strcmp(certfile, privkeyfile) == 0) {
274         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
275             "Error: Certificate and privatekey cannot be saved in the same file!\n");
276         return -1;
277     }
278
279     fp = fopen(privkeyfile, "w");
280     if (fp == NULL) {
281         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
282             "Failed to open file %s \n", privkeyfile );
283         return UPNP_E_FILE_NOT_FOUND;
284     }
285
286     // export private key and certificate
287     ret = gnutls_x509_privkey_export(*key, GNUTLS_X509_FMT_PEM, buffer, &buffer_size);
288     if (ret < 0) {
289         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
290             "gnutls_x509_privkey_export failed. %s \n", gnutls_strerror(ret) );
291         fclose(fp);
292         return ret;
293     }
294     fprintf(fp, "%s", buffer);
295     fclose(fp);
296
297     // export cert(s)
298     fp = fopen(certfile, "w");
299     if (fp == NULL) {
300         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
301              "Error: Failed to open file %s \n", privkeyfile );
302         return UPNP_E_FILE_NOT_FOUND;
303     }
304
305     ret = gnutls_x509_crt_export(*crt, GNUTLS_X509_FMT_PEM, buffer, &buffer_size);
306     if (ret < 0) {
307         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
308              "Error: gnutls_x509_crt_export failed. %s\n", gnutls_strerror(ret) );
309         return ret;
310     }
311     fprintf(fp, "%s", buffer);
312
313     // Is CA certificate appended?
314     if (append_CA)
315     {
316         *buffer = '\0';
317         buffer_size = orig_size;
318         ret = gnutls_x509_crt_export(ca_crt, GNUTLS_X509_FMT_PEM, buffer, &buffer_size);
319         if (ret < 0) {
320             UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
321                 "Error: gnutls_x509_crt_export failed. %s\n", gnutls_strerror(ret) );
322             return ret;
323         }
324         fprintf(fp, "%s", buffer);
325     }
326     fclose(fp);
327
328     return 0;
329 }
330
331 /************************************************************************
332 *   Function :  create_certificate
333 *
334 *   Parameters :
335 *       OUT gnutls_x509_crt_t *crt     ;  Pointer to gnutls_x509_crt_t where certificate is created
336 *       OUT gnutls_x509_privkey_t *key ;  Pointer to gnutls_x509_privkey_t where private key is created
337 *       IN char *CN                    ;  Common Name velue in certificate
338 *       IN int modulusBits             ;  Size of modulus in certificate
339 *       IN unsigned long lifetime      ;  How many seconds until certificate will expire. Counted from now.
340 *
341 *   Description :   Creates the certificate and private key
342 *
343 *   Return : int ;
344 *       UPNP or gnutls error code.
345 *
346 *   Note :
347 ************************************************************************/
348 static int create_certificate(gnutls_x509_crt_t *crt, gnutls_x509_privkey_t *key, const char *CN, const int modulusBits, const unsigned long lifetime, const void *purpose, unsigned int key_usage, unsigned int is_ca)
349 {
350     unsigned char buffer[10 * 1024];
351     int ret, serial;
352
353     // create private key
354     ret = gnutls_x509_privkey_generate (*key, GNUTLS_PK_RSA, modulusBits, 0);
355     if (ret < 0) {
356         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
357              "Error: gnutls_x509_privkey_generate failed. %s", gnutls_strerror(ret) );
358         return ret;
359     }
360
361     // set common name
362     ret = gnutls_x509_crt_set_dn_by_oid (*crt, GNUTLS_OID_X520_COMMON_NAME, 0, CN, strlen(CN));
363     if (ret < 0) {
364         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
365              "Error: gnutls_x509_crt_set_dn_by_oid failed. %s", gnutls_strerror(ret) );
366         return ret;
367     }
368
369     // set private key for cert
370     ret = gnutls_x509_crt_set_key (*crt, *key);
371     if (ret < 0) {
372         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
373              "Error: gnutls_x509_crt_set_key failed. %s", gnutls_strerror(ret) );
374         return ret;
375     }
376
377     ret = gnutls_x509_crt_set_activation_time (*crt, time (NULL));
378     if (ret < 0) {
379         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
380              "Error: gnutls_x509_crt_set_activation_time. %s", gnutls_strerror(ret) );
381         return ret;
382     }
383
384 // this tries to solve Year 2038 problem with "too big" unix timestamps http://en.wikipedia.org/wiki/Year_2038_problem
385 #ifdef UPNP_X509_CERT_ULTIMATE_EXPIRE_DATE
386     ret = gnutls_x509_crt_set_expiration_time (*crt, UPNP_X509_CERT_ULTIMATE_EXPIRE_DATE);
387 #else
388     ret = gnutls_x509_crt_set_expiration_time (*crt, time (NULL) + lifetime);
389 #endif
390     if (ret < 0) {
391         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
392              "Error: gnutls_x509_crt_set_expiration_time failed. %s", gnutls_strerror(ret) );
393         return ret;
394     }
395
396     //serial
397     serial = time (NULL);
398     buffer[4] = serial & 0xff;
399     buffer[3] = (serial >> 8) & 0xff;
400     buffer[2] = (serial >> 16) & 0xff;
401     buffer[1] = (serial >> 24) & 0xff;
402     buffer[0] = 0;
403
404     ret = gnutls_x509_crt_set_serial (*crt, buffer, 5);
405     if (ret < 0) {
406         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
407              "Error: gnutls_x509_crt_set_serial failed. %s", gnutls_strerror(ret) );
408         return ret;
409     }
410
411     if (purpose)
412     {
413         ret = gnutls_x509_crt_set_key_purpose_oid (*crt, purpose, 0);
414         if (ret < 0) {
415             UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
416                 "Error: gnutls_x509_crt_set_key_purpose_oid failed. %s", gnutls_strerror(ret) );
417             return ret;
418         }
419     }
420
421     ret = gnutls_x509_crt_set_key_usage (*crt, key_usage);
422     if (ret < 0) {
423         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
424              "Error: gnutls_x509_crt_set_key_usage failed. %s", gnutls_strerror(ret) );
425         return ret;
426     }
427
428     if (is_ca)
429     {
430         // if ceritficate is used as CA
431         ret = gnutls_x509_crt_set_ca_status (*crt, is_ca);
432         if (ret < 0) {
433             UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
434                 "Error: gnutls_x509_crt_set_ca_status failed. %s", gnutls_strerror(ret) );
435             return ret;
436         }
437     }
438
439     // set version
440     ret = gnutls_x509_crt_set_version(*crt, UPNP_X509_CERT_VERSION);
441     if (ret < 0) {
442         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
443              "Error: gnutls_x509_crt_set_version failed. %s", gnutls_strerror(ret) );
444         return ret;
445     }
446
447     return 0;
448 }
449
450 /************************************************************************
451 *   Function :  create_new_certificate
452 *
453 *   Parameters :
454 *       OUT gnutls_x509_crt_t *crt     ;  Pointer to gnutls_x509_crt_t where certificate is created
455 *       OUT gnutls_x509_privkey_t *key ;  Pointer to gnutls_x509_privkey_t where private key is created
456 *       IN const char directory        ;  Directory where files locate. If directory doesn't exist, tries to create. Must contain trailing '/'
457 *       IN const char *certfile        ;  Full path to file where certificate is exported in PEM format
458 *       IN const char *privkeyfile     ;  Full path to file where private key is exported in PEM format
459 *       IN char *CN                    ;  Common Name velue in certificate
460 *       IN int modulusBits             ;  Size of modulus in certificate
461 *       IN unsigned long lifetime      ;  How many seconds until certificate will expire. Counted from now.
462 *       IN int is_client               ;  Is created certificate client certificate. Affects to purpose of certificate.
463 *
464 *   Description :   Create new self signed certificate. Creates also new private key.
465 *
466 *   Return : int ;
467 *       UPNP or gnutls error code.
468 *
469 *   Note :
470 ************************************************************************/
471 static int create_new_certificate(gnutls_x509_crt_t *crt, gnutls_x509_privkey_t *key, const char *directory, const char *certfile, const char *privkeyfile, const char *CN, const int modulusBits, const unsigned long lifetime, int is_client)
472 {
473     int ret;
474
475     // create dir if doesn't exist yet
476     ret = mkdir(directory, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
477     if (ret != 0 && errno != EEXIST) {
478         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
479             "Failed to create certificate directory %s \n", directory );
480         return UPNP_E_FILE_NOT_FOUND;
481     }
482
483     // first we create CA certificate and key if those doesn't exist yet
484     if (ca_crt == NULL || ca_privkey == NULL)
485     {
486         int dirlen = strlen(directory);
487
488         // add trailing '/' if directory doesn't have it yet
489         char tmpDir[dirlen+1];
490         strcpy(tmpDir,directory);
491         if (directory[dirlen-1] != '/')
492         {
493             strcat(tmpDir, "/");
494             dirlen = strlen(tmpDir);
495         }
496
497         char tmp_certfile[dirlen+strlen(UPNP_X509_CA_CERT_FILE)];
498         char tmp_privkeyfile[dirlen+strlen(UPNP_X509_CA_PRIVKEY_FILE)];
499
500         strcpy(tmp_certfile, tmpDir);
501         strcat(tmp_certfile,UPNP_X509_CA_CERT_FILE);
502
503         strcpy(tmp_privkeyfile, tmpDir);
504         strcat(tmp_privkeyfile,UPNP_X509_CA_PRIVKEY_FILE);
505
506         // init private key
507         ret = gnutls_x509_privkey_init (&ca_privkey);
508         if (ret < 0) {
509             UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
510                 "Error: gnutls_x509_privkey_init failed. %s", gnutls_strerror(ret) );
511             return ret;
512         }
513
514         //init certificate
515         ret = gnutls_x509_crt_init (&ca_crt);
516         if (ret < 0) {
517             UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
518                 "Error: gnutls_x509_crt_init failed. %s", gnutls_strerror(ret) );
519             return ret;
520         }
521         int createNewCA = 1;
522         gnutls_datum_t pem_data = {NULL, 0};
523         //  import CA private key from file
524         ret = read_pem_data_file(tmp_privkeyfile, &pem_data);
525         if (ret == 0) {
526             ret = gnutls_x509_privkey_import(ca_privkey, &pem_data, GNUTLS_X509_FMT_PEM);
527             if (ret == GNUTLS_E_SUCCESS ) {
528                 // import CA certificate from file
529                 ret = read_pem_data_file(tmp_certfile, &pem_data);
530                 if (ret == 0) {
531                     ret = gnutls_x509_crt_import(ca_crt, &pem_data, GNUTLS_X509_FMT_PEM);
532                     if (ret == GNUTLS_E_SUCCESS) {
533                         createNewCA = 0; // no need to create new CA certificate and key
534                     }
535                 }
536             }
537         }
538
539         if (createNewCA)
540         {
541             // create ca certificate
542             ret = create_certificate(&ca_crt, &ca_privkey, UPNP_CA_CERT_CN, modulusBits, lifetime, NULL, GNUTLS_KEY_KEY_CERT_SIGN, 1);
543             if (ret < 0) {
544                 UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
545                     "Error: CA cert, Failed to create certificate. %s", gnutls_strerror(ret) );
546                 return ret;
547             }
548
549             // self sign certificate
550             ret = gnutls_x509_crt_sign2 (ca_crt, ca_crt, ca_privkey, GNUTLS_DIG_SHA256, 0);
551             if (ret < 0) {
552                 UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
553                     "Error: CA cert, gnutls_x509_crt_sign2 failed. %s", gnutls_strerror(ret) );
554                 return ret;
555             }
556
557             ret = export_certificate_to_file(&ca_crt, &ca_privkey, tmp_certfile, tmp_privkeyfile, 0);
558         }
559     }
560
561     // create the certificate
562     if (is_client)
563         ret = create_certificate(crt, key, CN, modulusBits, lifetime, GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KEY_DIGITAL_SIGNATURE, 0);
564     else
565         ret = create_certificate(crt, key, CN, modulusBits, lifetime, GNUTLS_KP_TLS_WWW_SERVER, GNUTLS_KEY_DIGITAL_SIGNATURE, 0);
566     if (ret < 0) {
567         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
568                 "Error: Failed to create certificate. %s", gnutls_strerror(ret) );
569         return ret;
570     }
571
572     // sign certificate
573     ret = gnutls_x509_crt_sign2 (*crt, ca_crt, ca_privkey, GNUTLS_DIG_SHA256, 0);
574     if (ret < 0) {
575         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
576                 "Error: gnutls_x509_crt_sign2 failed. %s", gnutls_strerror(ret) );
577         return ret;
578     }
579
580     ret = export_certificate_to_file(crt, key, certfile, privkeyfile, 1);
581
582     return ret;
583 }
584
585
586 /************************************************************************
587 *   Function :  init_x509_certificate_credentials
588 *
589 *   Parameters :
590 *       OUT gnutls_certificate_credentials_t *x509_cred     ;  Pointer to gnutls_certificate_credentials_t where certificate credentials are inserted
591 *       IN const char *directory       ;  Path to directory where files locate or where files are created      
592 *       IN const char *CertFile        ;  Selfsigned certificate file of client
593 *       IN const char *PrivKeyFile     ;  Private key file of client.
594 *       IN const char *TrustFile       ;  File containing trusted certificates. (PEM format)
595 *       IN const char *CRLFile         ;  Certificate revocation list. Untrusted certificates. (PEM format)
596 *
597 *   Description :   Init gnutls_certificate_credentials_t structure for use with 
598 *       input from given parameter files. All files may be NULL
599 *
600 *   Return : int ;
601 *       UPNP or gnutls error code.
602 *
603 *   Note :
604 ************************************************************************/
605 int init_x509_certificate_credentials(gnutls_certificate_credentials_t *x509_cred, const char *directory, const char *CertFile, const char *PrivKeyFile, const char *TrustFile, const char *CRLFile)
606 {
607     int ret;
608     int dirlen = strlen(directory);
609
610     // add trailing '/' if directory doesn't have it yet
611     char tmpDir[dirlen+1];
612     strcpy(tmpDir,directory);
613     if (directory[dirlen-1] != '/')
614     {
615         strcat(tmpDir, "/");
616         dirlen = strlen(tmpDir);
617     }
618
619     ret = gnutls_certificate_allocate_credentials (x509_cred);
620     if ( ret != GNUTLS_E_SUCCESS ) {
621         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
622             "StartHttpsServer: gnutls_certificate_allocate_credentials failed. (%s) \n\n", gnutls_strerror(ret) );
623         return ret;
624     }
625
626     if (TrustFile) {
627         char tmp_trustfile[dirlen+strlen(TrustFile)];
628         strcpy(tmp_trustfile, tmpDir);
629         strcat(tmp_trustfile,TrustFile);
630
631         ret = gnutls_certificate_set_x509_trust_file (*x509_cred, tmp_trustfile, GNUTLS_X509_FMT_PEM); // white list
632         if (ret < 0) {
633             UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
634                 "StartHttpsServer: gnutls_certificate_set_x509_trust_file failed (%s)\n\n", gnutls_strerror (ret));
635             return ret;
636         }
637     }
638
639     if (CRLFile) {
640         char tmp_crlfile[dirlen+strlen(CRLFile)];
641         strcpy(tmp_crlfile, tmpDir);
642         strcat(tmp_crlfile,CRLFile);
643
644         ret = gnutls_certificate_set_x509_crl_file (*x509_cred, tmp_crlfile, GNUTLS_X509_FMT_PEM); // black list
645         if (ret < 0) {
646             UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
647                 "StartHttpsServer: gnutls_certificate_set_x509_crl_file failed. (%s)\n\n", gnutls_strerror (ret));
648             return ret;
649         }
650     }
651
652     if (CertFile && PrivKeyFile) {
653         char tmp_certfile[dirlen+strlen(CertFile)];
654         char tmp_privkeyfile[dirlen+strlen(PrivKeyFile)];
655
656         strcpy(tmp_certfile, tmpDir);
657         strcat(tmp_certfile,CertFile);
658
659         strcpy(tmp_privkeyfile, tmpDir);
660         strcat(tmp_privkeyfile,PrivKeyFile);
661
662         ret = gnutls_certificate_set_x509_key_file (*x509_cred, tmp_certfile, tmp_privkeyfile, GNUTLS_X509_FMT_PEM);
663         if (ret != GNUTLS_E_SUCCESS) {
664             UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
665                 "StartHttpsServer: gnutls_certificate_set_x509_key_file failed. (%s)\n\n", gnutls_strerror (ret));
666             return ret;
667         }
668     }
669
670     return 0;
671 }
672
673
674
675 /************************************************************************
676 *   Function :  load_x509_self_signed_certificate
677 *
678 *   Parameters :
679 *       OUT gnutls_x509_crt_t *crt     ;  Pointer to gnutls_x509_crt_t table where certificate(s) is created
680         INOUT unsigned int *crt_size   ;  IN: how many certificates crt can have at maximum. OUT: How many certificates crt has.
681 *       OUT gnutls_x509_privkey_t *key ;  Pointer to gnutls_x509_privkey_t where private key is created
682 *       IN const char *directory       ;  Path to directory where files locate or where files are created.
683 *       IN const char *certfile        ;  Name of file where certificate is exported in PEM format
684 *       IN const char *privkeyfile     ;  Name of file where private key is exported in PEM format
685 *       IN char *CN                    ;  Common Name velue in certificate
686 *       IN int modulusBits             ;  Size of modulus in certificate
687 *       IN unsigned long lifetime      ;  How many seconds until certificate will expire. Counted from now.
688 *       IN int is_client               ;  Is created certificate client certificate. Affects to purpose of certificate.
689
690 *   Description :   Create self signed certificate. For this private key is also created.
691 *           If certfile already contains certificate and privkeyfile contains privatekey,
692 *           function uses that certificate. If only other is defined, then both will be created.
693 *
694 *   Return : int ;
695 *       UPNP or gnutls error code.
696 *
697 *   Note :
698 ************************************************************************/
699 int load_x509_self_signed_certificate(gnutls_x509_crt_t *crt, unsigned int *crt_size, gnutls_x509_privkey_t *key, const char *directory, const char *certfile, const char *privkeyfile, const char *CN, const int modulusBits, const unsigned long lifetime, int is_client)
700 {
701     int cert_ok = 0;
702     int ret = 0;
703     gnutls_datum_t pem_data = {NULL, 0};
704     int dirlen = strlen(directory);
705
706     // add trailing '/' if directory doesn't have it yet
707     char tmpDir[dirlen+1];
708     strcpy(tmpDir,directory);
709     if (directory[dirlen-1] != '/')
710     {
711         strcat(tmpDir, "/");
712         dirlen = strlen(tmpDir);
713     }
714
715     char tmp_certfile[dirlen+strlen(certfile)];
716     char tmp_privkeyfile[dirlen+strlen(privkeyfile)];
717
718     strcpy(tmp_certfile, tmpDir);
719     strcat(tmp_certfile,certfile);
720
721     strcpy(tmp_privkeyfile, tmpDir);
722     strcat(tmp_privkeyfile,privkeyfile);
723
724     // create temporary certificate.
725     gnutls_x509_crt_t tmp_crt;
726     //init certificate
727     ret = gnutls_x509_crt_init (&tmp_crt);
728     if (ret < 0) {
729         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
730                 "Error: gnutls_x509_crt_init failed. %s", gnutls_strerror(ret) );
731         return ret;
732     }
733
734     // init private key
735     ret = gnutls_x509_privkey_init (key);
736     if (ret < 0) {
737         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
738             "gnutls_x509_privkey_init failed. %s \n", gnutls_strerror(ret) );
739         return ret;
740     }
741
742     // import private key from file
743     ret = read_pem_data_file(tmp_privkeyfile, &pem_data);
744     if (ret == 0) {
745         ret = gnutls_x509_privkey_import(*key, &pem_data, GNUTLS_X509_FMT_PEM);
746         if (ret < 0) {
747             UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
748                 "gnutls_x509_privkey_import failed. %s \n", gnutls_strerror(ret) );
749             if (pem_data.data) free(pem_data.data); //release privkey, it's not needed anymore
750             return ret;
751         }
752
753         // import certificate from file
754         ret = read_pem_data_file(tmp_certfile, &pem_data);
755         if (ret == 0) {
756             //ret = gnutls_x509_crt_import(*crt, &pem_data, GNUTLS_X509_FMT_PEM);
757             gnutls_x509_crt_list_import (crt, crt_size, &pem_data,
758                      GNUTLS_X509_FMT_PEM,
759                      GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
760             if (ret < 0) {
761                 UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
762                     "Error: gnutls_x509_crt_list_import failed. %s", gnutls_strerror(ret) );
763                 if (pem_data.data) free(pem_data.data);
764                 return ret;
765             }
766             /* this validation was done when there was only one certificate not whole chain. 
767             Now we should check validity of the certificate chain?
768             ret = validate_x509_certificate(crt, NULL, CN);
769             if (ret < 0) {
770                 UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
771                     "Error: X.509 certificate validation failed. %s", gnutls_strerror(ret) );
772                 if (pem_data.data) free(pem_data.data); 
773                 return ret;
774             }
775             */
776             cert_ok = 1;
777         }
778         else {
779             ret = create_new_certificate(&tmp_crt, key, tmpDir, tmp_certfile, tmp_privkeyfile, CN, modulusBits, lifetime, is_client);
780         }
781     }
782     else {
783         ret = create_new_certificate(&tmp_crt, key, tmpDir, tmp_certfile, tmp_privkeyfile, CN, modulusBits, lifetime, is_client);
784     }
785
786     if (!cert_ok)
787     {
788         // do the importing again so that we get the whole chain from the file
789         // TODO improve this so that we dont need this import here
790         ret = read_pem_data_file(tmp_certfile, &pem_data);
791         if (ret == 0) {
792             //ret = gnutls_x509_crt_import(*crt, &pem_data, GNUTLS_X509_FMT_PEM);
793             gnutls_x509_crt_list_import (crt, crt_size, &pem_data,
794                     GNUTLS_X509_FMT_PEM,
795                     GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
796             if (ret < 0) {
797                 UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
798                     "Error: gnutls_x509_crt_list_import failed. %s", gnutls_strerror(ret) );
799                 if (pem_data.data) free(pem_data.data);
800                 return ret;
801             }
802         }
803     }
804
805     gnutls_x509_crt_deinit(tmp_crt);
806     if (pem_data.data) free(pem_data.data);
807     return ret;
808 }
809
810
811 /************************************************************************
812 *   Function :  validate_x509_certificate
813 *
814 *   Parameters :
815 *       IN const gnutls_x509_crt_t *crt  ;  Pointer to certificate which is validated
816 *       IN const char *hostname          ;  Hostname to compare with certificates subject
817 *       IN const char *commonname        ;  CN value which is compared with subject CN value of certificate 
818
819 *   Description :   Check that given certificate is activated (not before > now), certificate 
820 *       has not expired (not after < now). If hostname or commonname are defined check that
821 *       those values match values found from certificate. Hostname check is "a basic implementation 
822 *       of the matching described in RFC2818 (HTTPS), which takes into account wildcards, and the 
823 *       DNSName/IPAddress subject alternative name PKIX extension." (gnutls)
824 *       Commonname check just checks if commonname value equals CN found from certificates subject.
825 *
826 *   Return : int ;
827 *       UPNP or gnutls error code.
828 *
829 *   Note :
830 ************************************************************************/
831 int validate_x509_certificate(const gnutls_x509_crt_t *crt, const char *hostname, const char *commonname)
832 {
833     int ret = 0;
834     size_t buf_size = 20;
835     char buf[buf_size];
836
837     if (gnutls_x509_crt_get_expiration_time (*crt) < time (NULL)) {
838         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
839             "Certificate has expired\n");
840         return GNUTLS_E_X509_CERTIFICATE_ERROR;
841     }
842
843     if (gnutls_x509_crt_get_activation_time (*crt) > time (NULL))  {
844         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
845             "Certificate is not yet activated\n");
846         return GNUTLS_E_X509_CERTIFICATE_ERROR;
847     }
848
849     if (hostname && (strlen(hostname) > 0)) {
850         if (!gnutls_x509_crt_check_hostname (*crt, hostname)) {
851             UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
852                 "Certificate's owner does not match hostname '%s'\n",hostname);
853             return GNUTLS_E_X509_CERTIFICATE_ERROR;
854         }
855     }
856
857     if (commonname) {
858         ret = gnutls_x509_crt_get_dn_by_oid (*crt, GNUTLS_OID_X520_COMMON_NAME, 0, 0, buf, &buf_size);
859         if (ret != 0) {
860             UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
861                 "Failed to get certificates Common Name value\n"); 
862             return ret;
863         }
864
865         if (strcmp(buf, commonname) != 0) {
866             UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
867                 "Certificate's Common Name '%s' isn't what expected '%s'\n",buf,commonname);
868             return GNUTLS_E_X509_CERTIFICATE_ERROR;
869         }
870     }
871
872     return ret;
873 }
874
875
876 /************************************************************************
877 *   Function :  get_peer_certificate
878 *
879 *   Parameters :
880 *       IN gnutls_session_t session  ;  SSL session
881 *       OUT unsigned char *data      ;  Certificate is returned in DER format here
882 *       INOUT int *data_size         ;  Pointer to integer which represents length of certificate
883 *       OUT char **CN                ;  Pointer to string where Common Name value from peer certificate is put. If NULL this is ignored. 
884
885 *   Description :   Export peer certificate to given parameter. When calling this
886 *       data must have enough memory allocated and data_size must contain info
887 *       how much data has space.
888 *
889 *   Return : int ;
890 *       UPNP or gnutls error code.
891 *
892 *   Note :
893 ************************************************************************/
894 int get_peer_certificate(gnutls_session_t session, unsigned char *data, int *data_size, char **CN)
895 {
896     const gnutls_datum_t *cert_list;
897     unsigned int cert_list_size;
898     int ret;
899     gnutls_x509_crt_t cert;
900
901     if ((ret = gnutls_certificate_type_get (session)) != GNUTLS_CRT_X509)
902     {
903         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
904             "Peer certificate type must be X.509. Wrong type received.\n");
905         return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
906     }
907
908     // get certificate list. First in list is peers
909     cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
910     if (cert_list == NULL || cert_list_size < 1)
911     {
912         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
913             "Could not get peers certificate\n");
914         return GNUTLS_E_X509_CERTIFICATE_ERROR;
915     }
916
917     //init certificate
918     ret = gnutls_x509_crt_init (&cert);
919     if (ret < 0) {
920         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
921             "gnutls_x509_crt_init failed. %s \n", gnutls_strerror(ret) );
922         return ret;
923     }
924
925     // first in the list is peers certificate
926     ret = gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER);
927     if (ret < 0)
928     {
929         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
930             "gnutls_x509_crt_import failed. %s \n", gnutls_strerror(ret) );
931         gnutls_x509_crt_deinit (cert);
932         return ret;
933     }
934
935     // export certificate to data
936     ret = gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_DER, data, (size_t *)data_size);
937     if (ret < 0) {
938         UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
939             "gnutls_x509_crt_export failed. %s \n", gnutls_strerror(ret) );
940         gnutls_x509_crt_deinit (cert);
941         return ret;
942     }
943
944     // get Common name value from certificate
945     if (CN != NULL)
946     {
947         int CN_size = 50;
948         *CN = (char *)malloc(CN_size);
949         ret = gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, *CN, (size_t *)&CN_size);
950         if (ret != 0) {
951             UpnpPrintf( UPNP_CRITICAL, X509, __FILE__, __LINE__,
952                 "Failed to get certificates Common Name value\n");
953             gnutls_x509_crt_deinit (cert);
954             return ret;
955         }
956     }
957
958     gnutls_x509_crt_deinit (cert);
959
960     return 0;
961 }