/src/gnutls/lib/tls13/certificate_verify.c
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | /*  | 
2  |  |  * Copyright (C) 2017 Red Hat, Inc.  | 
3  |  |  *  | 
4  |  |  * Author: Nikos Mavrogiannopoulos  | 
5  |  |  *  | 
6  |  |  * This file is part of GnuTLS.  | 
7  |  |  *  | 
8  |  |  * The GnuTLS is free software; you can redistribute it and/or  | 
9  |  |  * modify it under the terms of the GNU Lesser General Public License  | 
10  |  |  * as published by the Free Software Foundation; either version 2.1 of  | 
11  |  |  * the License, or (at your option) any later version.  | 
12  |  |  *  | 
13  |  |  * This library is distributed in the hope that it will be useful, but  | 
14  |  |  * WITHOUT ANY WARRANTY; without even the implied warranty of  | 
15  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU  | 
16  |  |  * Lesser General Public License for more details.  | 
17  |  |  *  | 
18  |  |  * You should have received a copy of the GNU Lesser General Public License  | 
19  |  |  * along with this program.  If not, see <https://www.gnu.org/licenses/>  | 
20  |  |  *  | 
21  |  |  */  | 
22  |  |  | 
23  |  | #include "gnutls_int.h"  | 
24  |  | #include "errors.h"  | 
25  |  | #include "handshake.h"  | 
26  |  | #include "auth/cert.h"  | 
27  |  | #include "ext/signature.h"  | 
28  |  | #include "algorithms.h"  | 
29  |  | #include "tls13-sig.h"  | 
30  |  | #include "mbuffers.h"  | 
31  |  | #include "tls13/certificate_verify.h"  | 
32  |  |  | 
33  |  | #define SRV_CTX "TLS 1.3, server CertificateVerify"  | 
34  |  | static const gnutls_datum_t srv_ctx = { (void *)SRV_CTX, sizeof(SRV_CTX) - 1 }; | 
35  |  |  | 
36  |  | #define CLI_CTX "TLS 1.3, client CertificateVerify"  | 
37  |  | static const gnutls_datum_t cli_ctx = { (void *)CLI_CTX, sizeof(CLI_CTX) - 1 }; | 
38  |  |  | 
39  |  | int _gnutls13_recv_certificate_verify(gnutls_session_t session)  | 
40  | 0  | { | 
41  | 0  |   int ret;  | 
42  | 0  |   gnutls_buffer_st buf;  | 
43  | 0  |   const gnutls_sign_entry_st *se;  | 
44  | 0  |   gnutls_datum_t sig_data;  | 
45  | 0  |   gnutls_certificate_credentials_t cred;  | 
46  | 0  |   unsigned vflags;  | 
47  | 0  |   gnutls_pcert_st peer_cert;  | 
48  | 0  |   cert_auth_info_t info =  | 
49  | 0  |     _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);  | 
50  | 0  |   bool server = 0;  | 
51  | 0  |   gnutls_certificate_type_t cert_type;  | 
52  |  | 
  | 
53  | 0  |   memset(&peer_cert, 0, sizeof(peer_cert));  | 
54  |  |  | 
55  |  |   /* this message is only expected if we have received  | 
56  |  |    * a certificate message */  | 
57  | 0  |   if (!(session->internals.hsk_flags & HSK_CRT_VRFY_EXPECTED))  | 
58  | 0  |     return 0;  | 
59  |  |  | 
60  | 0  |   if (session->security_parameters.entity == GNUTLS_SERVER)  | 
61  | 0  |     server = 1;  | 
62  |  | 
  | 
63  | 0  |   cred = (gnutls_certificate_credentials_t)_gnutls_get_cred(  | 
64  | 0  |     session, GNUTLS_CRD_CERTIFICATE);  | 
65  | 0  |   if (unlikely(cred == NULL))  | 
66  | 0  |     return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);  | 
67  | 0  |   if (unlikely(info == NULL))  | 
68  | 0  |     return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);  | 
69  |  |  | 
70  | 0  |   ret = _gnutls_recv_handshake(  | 
71  | 0  |     session, GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY, 0, &buf);  | 
72  | 0  |   if (ret < 0)  | 
73  | 0  |     return gnutls_assert_val(ret);  | 
74  |  |  | 
75  | 0  |   _gnutls_handshake_log("HSK[%p]: Parsing certificate verify\n", session); | 
76  |  | 
  | 
77  | 0  |   if (buf.length < 2) { | 
78  | 0  |     gnutls_assert();  | 
79  | 0  |     ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH;  | 
80  | 0  |     goto cleanup;  | 
81  | 0  |   }  | 
82  |  |  | 
83  | 0  |   se = _gnutls_tls_aid_to_sign_entry(buf.data[0], buf.data[1],  | 
84  | 0  |              get_version(session));  | 
85  | 0  |   if (se == NULL) { | 
86  | 0  |     _gnutls_handshake_log("Found unsupported signature (%d.%d)\n", | 
87  | 0  |               (int)buf.data[0], (int)buf.data[1]);  | 
88  | 0  |     ret = gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);  | 
89  | 0  |     goto cleanup;  | 
90  | 0  |   }  | 
91  |  |  | 
92  | 0  |   if (server)  | 
93  | 0  |     gnutls_sign_algorithm_set_client(session, se->id);  | 
94  | 0  |   else  | 
95  | 0  |     gnutls_sign_algorithm_set_server(session, se->id);  | 
96  |  | 
  | 
97  | 0  |   buf.data += 2;  | 
98  | 0  |   buf.length -= 2;  | 
99  |  |  | 
100  |  |   /* we check during verification whether the algorithm is enabled */  | 
101  |  | 
  | 
102  | 0  |   ret = _gnutls_buffer_pop_datum_prefix16(&buf, &sig_data);  | 
103  | 0  |   if (ret < 0) { | 
104  | 0  |     gnutls_assert();  | 
105  | 0  |     goto cleanup;  | 
106  | 0  |   }  | 
107  |  |  | 
108  | 0  |   if (sig_data.size == 0) { | 
109  | 0  |     gnutls_assert();  | 
110  | 0  |     ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;  | 
111  | 0  |     goto cleanup;  | 
112  | 0  |   }  | 
113  |  |  | 
114  |  |   /* We verify the certificate of the peer. Therefore we need to  | 
115  |  |    * retrieve the negotiated certificate type for the peer. */  | 
116  | 0  |   cert_type = get_certificate_type(session, GNUTLS_CTYPE_PEERS);  | 
117  |  |  | 
118  |  |   /* Verify the signature */  | 
119  | 0  |   ret = _gnutls_get_auth_info_pcert(&peer_cert, cert_type, info);  | 
120  | 0  |   if (ret < 0) { | 
121  | 0  |     gnutls_assert();  | 
122  | 0  |     goto cleanup;  | 
123  | 0  |   }  | 
124  |  |  | 
125  | 0  |   vflags = cred->verify_flags |  | 
126  | 0  |      session->internals.additional_verify_flags;  | 
127  |  | 
  | 
128  | 0  |   ret = _gnutls13_handshake_verify_data(session, vflags, &peer_cert,  | 
129  | 0  |                 server ? (&cli_ctx) : (&srv_ctx),  | 
130  | 0  |                 &sig_data, se);  | 
131  | 0  |   if (ret < 0) { | 
132  | 0  |     gnutls_assert();  | 
133  | 0  |     goto cleanup;  | 
134  | 0  |   }  | 
135  |  |  | 
136  | 0  |   if (buf.length > 0) { | 
137  | 0  |     gnutls_assert();  | 
138  | 0  |     ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH;  | 
139  | 0  |     goto cleanup;  | 
140  | 0  |   }  | 
141  |  |  | 
142  | 0  |   ret = 0;  | 
143  | 0  | cleanup:  | 
144  | 0  |   gnutls_pcert_deinit(&peer_cert);  | 
145  | 0  |   _gnutls_buffer_clear(&buf);  | 
146  | 0  |   return ret;  | 
147  | 0  | }  | 
148  |  |  | 
149  |  | int _gnutls13_send_certificate_verify(gnutls_session_t session, unsigned again)  | 
150  | 0  | { | 
151  | 0  |   int ret;  | 
152  | 0  |   gnutls_pcert_st *apr_cert_list;  | 
153  | 0  |   gnutls_privkey_t apr_pkey;  | 
154  | 0  |   int apr_cert_list_length;  | 
155  | 0  |   mbuffer_st *bufel = NULL;  | 
156  | 0  |   gnutls_buffer_st buf;  | 
157  | 0  |   gnutls_datum_t sig = { NULL, 0 }; | 
158  | 0  |   gnutls_sign_algorithm_t algo;  | 
159  | 0  |   const gnutls_sign_entry_st *se;  | 
160  | 0  |   bool server = 0;  | 
161  |  | 
  | 
162  | 0  |   if (again == 0) { | 
163  | 0  |     if (!session->internals.initial_negotiation_completed &&  | 
164  | 0  |         session->internals.hsk_flags & HSK_PSK_SELECTED)  | 
165  | 0  |       return 0;  | 
166  |  |  | 
167  | 0  |     if (session->security_parameters.entity == GNUTLS_SERVER &&  | 
168  | 0  |         session->internals.resumed)  | 
169  | 0  |       return 0;  | 
170  |  |  | 
171  | 0  |     if (session->security_parameters.entity == GNUTLS_SERVER)  | 
172  | 0  |       server = 1;  | 
173  |  | 
  | 
174  | 0  |     ret = _gnutls_get_selected_cert(session, &apr_cert_list,  | 
175  | 0  |             &apr_cert_list_length,  | 
176  | 0  |             &apr_pkey);  | 
177  | 0  |     if (ret < 0)  | 
178  | 0  |       return gnutls_assert_val(ret);  | 
179  |  |  | 
180  | 0  |     if (apr_cert_list_length == 0) { | 
181  | 0  |       if (server) { | 
182  | 0  |         return gnutls_assert_val(  | 
183  | 0  |           GNUTLS_E_INSUFFICIENT_CREDENTIALS);  | 
184  | 0  |       } else { | 
185  |  |         /* for client, this means either we  | 
186  |  |          * didn't get a cert request or we are  | 
187  |  |          * declining authentication; in either  | 
188  |  |          * case we don't send a cert verify */  | 
189  | 0  |         return 0;  | 
190  | 0  |       }  | 
191  | 0  |     }  | 
192  |  |  | 
193  | 0  |     if (server) { | 
194  | 0  |       algo = _gnutls_session_get_sign_algo(session,  | 
195  | 0  |                    &apr_cert_list[0],  | 
196  | 0  |                    apr_pkey, 0,  | 
197  | 0  |                    GNUTLS_KX_UNKNOWN);  | 
198  | 0  |       if (algo == GNUTLS_SIGN_UNKNOWN)  | 
199  | 0  |         return gnutls_assert_val(  | 
200  | 0  |           GNUTLS_E_INCOMPATIBLE_SIG_WITH_KEY);  | 
201  |  |  | 
202  | 0  |       gnutls_sign_algorithm_set_server(session, algo);  | 
203  | 0  |     } else { | 
204  |  |       /* for client, signature algorithm is already  | 
205  |  |        * determined from Certificate Request */  | 
206  | 0  |       algo = gnutls_sign_algorithm_get_client(session);  | 
207  | 0  |       if (unlikely(algo == GNUTLS_SIGN_UNKNOWN))  | 
208  | 0  |         return gnutls_assert_val(  | 
209  | 0  |           GNUTLS_E_INTERNAL_ERROR);  | 
210  | 0  |     }  | 
211  |  |  | 
212  | 0  |     se = _gnutls_sign_to_entry(algo);  | 
213  |  | 
  | 
214  | 0  |     ret = _gnutls13_handshake_sign_data(  | 
215  | 0  |       session, &apr_cert_list[0], apr_pkey,  | 
216  | 0  |       server ? (&srv_ctx) : (&cli_ctx), &sig, se);  | 
217  | 0  |     if (ret < 0)  | 
218  | 0  |       return gnutls_assert_val(ret);  | 
219  |  |  | 
220  | 0  |     ret = _gnutls_buffer_init_handshake_mbuffer(&buf);  | 
221  | 0  |     if (ret < 0) { | 
222  | 0  |       gnutls_assert();  | 
223  | 0  |       goto cleanup;  | 
224  | 0  |     }  | 
225  |  |  | 
226  | 0  |     ret = _gnutls_buffer_append_data(&buf, se->aid.id, 2);  | 
227  | 0  |     if (ret < 0) { | 
228  | 0  |       gnutls_assert();  | 
229  | 0  |       goto cleanup;  | 
230  | 0  |     }  | 
231  |  |  | 
232  | 0  |     ret = _gnutls_buffer_append_data_prefix(&buf, 16, sig.data,  | 
233  | 0  |               sig.size);  | 
234  | 0  |     if (ret < 0) { | 
235  | 0  |       gnutls_assert();  | 
236  | 0  |       goto cleanup;  | 
237  | 0  |     }  | 
238  |  |  | 
239  | 0  |     bufel = _gnutls_buffer_to_mbuffer(&buf);  | 
240  |  | 
  | 
241  | 0  |     gnutls_free(sig.data);  | 
242  | 0  |   }  | 
243  |  |  | 
244  | 0  |   return _gnutls_send_handshake(session, bufel,  | 
245  | 0  |               GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY);  | 
246  |  |  | 
247  | 0  | cleanup:  | 
248  | 0  |   gnutls_free(sig.data);  | 
249  | 0  |   _gnutls_buffer_clear(&buf);  | 
250  | 0  |   return ret;  | 
251  | 0  | }  |