2 * Copyright (C) 2007 Free Software Foundation
3 * Author: Simon Josefsson
5 * This file is part of GNUTLS.
7 * The GNUTLS library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public License
9 * as published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
25 * This file implements the authz extensions in
26 * draft-housley-tls-authz-extns-07 using the supplemental handshake
27 * record type, which see RFC 4680 and gnutls_supplemental.c.
29 * There are three parts of this file. The first is the client hello
30 * and server hello extensions, which are used to negotiate use of
31 * supplemental authz data. If they successfully negotiate that the
32 * client will send some format(s) and/or the server will send some
33 * format(s), this will request that gnutls_handshake() invoke a
34 * supplemental phase in the corresponding direction.
36 * It may be possible that client authz data format type negotiation
37 * fails, but server authz data format type negotiation succeeds. In
38 * that case, only the server will send supplemental data, and the
39 * client will only expect to receive supplemental data.
41 * The second part is parsing and generating the authz supplemental
42 * data itself, by using the callbacks.
44 * The third part is the public APIs for use in the callbacks, and of
45 * course gnutls_authz_enable() to request that authz should be used.
48 #include "gnutls_int.h"
49 #include "gnutls_auth_int.h"
50 #include "gnutls_errors.h"
51 #include "gnutls_num.h"
52 #include <ext_authz.h>
55 format_in_list_p (unsigned char format
,
56 const unsigned char *data
,
60 for (i
= 0; i
< data_size
; i
++)
61 if (format
== data
[i
])
67 recv_extension (gnutls_session_t session
,
73 const int *in
= formats
;
79 return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER
;
85 if (data_size
!= total_size
)
88 return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER
;
92 if (format_in_list_p (*in
- 1, data
, data_size
))
94 _gnutls_debug_log ("EXT[%x]: Keeping authz format %02x\n",
100 _gnutls_debug_log ("EXT[%x]: Disabling authz format %02x\n",
110 send_extension (gnutls_session_t session
,
115 ssize_t data_size
= _data_size
;
119 if (!authz_formats
[0])
122 /* Make room for size. */
123 DECR_LENGTH_RET (data_size
, 1, GNUTLS_E_SHORT_MEMORY_BUFFER
);
126 for (total_size
= 0; authz_formats
[total_size
]; total_size
++)
128 _gnutls_debug_log ("EXT[%x]: Sending authz format %02x\n",
129 session
, authz_formats
[total_size
]);
130 DECR_LENGTH_RET (data_size
, 1, GNUTLS_E_SHORT_MEMORY_BUFFER
);
131 *data
++ = authz_formats
[total_size
] - 1;
134 *sizepos
= total_size
;
136 return 1 + total_size
;
140 _gnutls_authz_ext_client_recv_params (gnutls_session_t session
,
144 int *client_formats
=
145 session
->security_parameters
.extensions
.authz_client_formats
;
148 ret
= recv_extension (session
, data
, data_size
, client_formats
);
154 if (session
->security_parameters
.entity
== GNUTLS_CLIENT
)
156 _gnutls_debug_log ("EXT[%x]: Will send supplemental data\n",
158 session
->security_parameters
.extensions
.do_send_supplemental
= 1;
161 session
->security_parameters
.extensions
.authz_recvd_client
= 1;
168 _gnutls_authz_ext_client_send_params (gnutls_session_t session
,
172 int *client_formats
=
173 session
->security_parameters
.extensions
.authz_client_formats
;
176 /* Should we be sending this? */
177 if (session
->security_parameters
.entity
== GNUTLS_SERVER
178 && !session
->security_parameters
.extensions
.authz_recvd_client
)
184 ret
= send_extension (session
, data
, _data_size
, client_formats
);
186 if (session
->security_parameters
.entity
== GNUTLS_SERVER
&& ret
> 0)
188 _gnutls_debug_log ("EXT[%x]: Will expect supplemental data\n",
190 session
->security_parameters
.extensions
.do_recv_supplemental
= 1;
197 _gnutls_authz_ext_server_recv_params (gnutls_session_t session
,
201 int *server_formats
=
202 session
->security_parameters
.extensions
.authz_server_formats
;
205 ret
= recv_extension (session
, data
, data_size
, server_formats
);
211 if (session
->security_parameters
.entity
== GNUTLS_CLIENT
)
213 _gnutls_debug_log ("EXT[%x]: Will expect supplemental data\n",
215 session
->security_parameters
.extensions
.do_recv_supplemental
= 1;
218 session
->security_parameters
.extensions
.authz_recvd_server
= 1;
225 _gnutls_authz_ext_server_send_params (gnutls_session_t session
,
229 int *server_formats
=
230 session
->security_parameters
.extensions
.authz_server_formats
;
233 /* Should we be sending this? */
234 if (session
->security_parameters
.entity
== GNUTLS_SERVER
235 && !session
->security_parameters
.extensions
.authz_recvd_server
)
241 ret
= send_extension (session
, data
, _data_size
, server_formats
);
243 if (session
->security_parameters
.entity
== GNUTLS_SERVER
&& ret
> 0)
245 _gnutls_debug_log ("EXT[%x]: Will send supplemental data\n",
247 session
->security_parameters
.extensions
.do_send_supplemental
= 1;
254 _gnutls_authz_supp_recv_params (gnutls_session_t session
,
258 int authz_formats
[MAX_AUTHZ_FORMATS
+ 1];
259 gnutls_datum_t info
[MAX_AUTHZ_FORMATS
];
260 gnutls_datum_t hash
[MAX_AUTHZ_FORMATS
];
261 int hashtype
[MAX_AUTHZ_FORMATS
];
262 ssize_t dsize
= data_size
;
263 const opaque
*p
= data
;
265 gnutls_authz_recv_callback_func callback
=
266 session
->security_parameters
.extensions
.authz_recv_callback
;
274 /* XXX Will there be more than one data item for each authz format?
275 If so, we can't know the maximum size of the list of authz data,
276 so replace the static arrays with dynamically allocated lists.
277 Let's worry about that when someone reports it. */
283 authz_formats
[i
] = _gnutls_read_uint16 (p
) + 1;
286 _gnutls_debug_log ("EXT[%x]: authz_format[%d]=%02x\n",
287 session
, i
, authz_formats
[i
]);
290 info
[i
].size
= _gnutls_read_uint16 (p
);
293 _gnutls_debug_log ("EXT[%x]: data[%d]=%d bytes\n",
294 session
, i
, info
[i
].size
);
298 DECR_LEN (dsize
, info
[i
].size
);
301 if (authz_formats
[i
] == GNUTLS_AUTHZ_X509_ATTR_CERT_URL
302 || authz_formats
[i
] == GNUTLS_AUTHZ_SAML_ASSERTION_URL
)
305 _gnutls_debug_log ("EXT[%x]: hashtype[%d]=%02x\n",
308 hashtype
[i
] = GNUTLS_MAC_SHA1
;
309 else if (*p
== '\x01')
310 hashtype
[i
] = GNUTLS_MAC_SHA256
;
314 return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER
;
319 hash
[i
].size
= _gnutls_hash_get_algo_len (hashtype
[i
]);
321 _gnutls_debug_log ("EXT[%x]: hash[%d]=%d\n",
322 session
, i
, hash
[i
].size
);
324 DECR_LEN (dsize
, hash
[i
].size
);
336 if (i
== MAX_AUTHZ_FORMATS
)
339 return GNUTLS_E_SHORT_MEMORY_BUFFER
;
344 authz_formats
[i
] = 0;
346 return callback (session
, authz_formats
, info
, hashtype
, hash
);
350 _gnutls_authz_supp_send_params (gnutls_session_t session
,
353 int *server_formats
=
354 session
->security_parameters
.extensions
.authz_server_formats
;
355 int *client_formats
=
356 session
->security_parameters
.extensions
.authz_client_formats
;
357 gnutls_authz_send_callback_func callback
=
358 session
->security_parameters
.extensions
.authz_send_callback
;
359 gnutls_buffer
*authz_buf
=
360 &session
->security_parameters
.extensions
.authz_data
;
366 return GNUTLS_E_INVALID_REQUEST
;
369 _gnutls_buffer_init (authz_buf
);
371 ret
= callback (session
, client_formats
, server_formats
);
378 ret
= _gnutls_buffer_append (buf
, authz_buf
->data
, authz_buf
->length
);
385 _gnutls_buffer_clear (authz_buf
);
391 add_data (gnutls_session_t session
,
394 gnutls_authz_data_format_type_t format
,
395 gnutls_mac_algorithm_t hash_type
,
398 gnutls_buffer
*buffer
= &session
->security_parameters
.extensions
.authz_data
;
399 size_t hash_len
= hash
? _gnutls_hash_get_algo_len (hash_type
) : 0;
400 unsigned char str
[4];
403 if (len
+ 4 > 0xFFFF)
406 return GNUTLS_E_INVALID_REQUEST
;
409 if (hash
&& hash_type
!= GNUTLS_MAC_SHA256
&& hash_type
!= GNUTLS_MAC_SHA1
)
412 return GNUTLS_E_INVALID_REQUEST
;
418 str
[2] = (len
>> 8) & 0xFF;
421 ret
= _gnutls_buffer_append (buffer
, str
, 4);
428 ret
= _gnutls_buffer_append (buffer
, data
, len
);
437 if (hash_type
== GNUTLS_MAC_SHA1
)
439 else if (hash_type
== GNUTLS_MAC_SHA256
)
442 ret
= _gnutls_buffer_append (buffer
, str
, 1);
449 ret
= _gnutls_buffer_append (buffer
, hash
, hash_len
);
461 * gnutls_authz_send_x509_attr_cert:
462 * @session: is a #gnutls_session_t structure.
463 * @data: buffer with a X.509 attribute certificate.
464 * @len: length of buffer.
466 * Send a X.509 attribute certificate as authorization data. This
467 * function may only be called inside a @send_callback set by
468 * gnutls_authz_enable().
470 * Returns: Returns 0 on success, or an error code on failures. If
471 * the supplied data was too long (the authorization extension only
472 * support 64kb large attribute certificates),
473 * %GNUTLS_E_INVALID_REQUEST is returned.
476 gnutls_authz_send_x509_attr_cert (gnutls_session_t session
,
480 return add_data (session
, data
, len
, GNUTLS_AUTHZ_X509_ATTR_CERT
, 0, NULL
);
484 * gnutls_authz_send_saml_assertion:
485 * @session: is a #gnutls_session_t structure.
486 * @data: buffer with a SAML assertion.
487 * @len: length of buffer.
489 * Send a SAML assertion as authorization data. This function may
490 * only be called inside a @send_callback set by
491 * gnutls_authz_enable().
493 * Returns: Returns 0 on success, or an error code on failures. If
494 * the supplied data was too long (the authorization extension only
495 * support 64kb large SAML assertions), %GNUTLS_E_INVALID_REQUEST is
499 gnutls_authz_send_saml_assertion (gnutls_session_t session
,
503 return add_data (session
, data
, len
, GNUTLS_AUTHZ_SAML_ASSERTION
, 0, NULL
);
507 * gnutls_authz_send_x509_attr_cert_url:
508 * @session: is a #gnutls_session_t structure.
509 * @url: buffer with a URL pointing to X.509 attribute certificate.
510 * @urllen: length of buffer.
511 * @hash_type: type of hash in @hash.
512 * @hash: buffer with hash of URL target.
514 * Send a URL to an X.509 attribute certificate as authorization data,
515 * including a hash used to make sure the retrieved data was the
516 * intended data. This function may only be called inside a
517 * @send_callback set by gnutls_authz_enable().
519 * Returns: Returns 0 on success, or an error code on failures. If
520 * the supplied data was too long (the authorization extension only
521 * support 64kb large URLs), %GNUTLS_E_INVALID_REQUEST is returned.
524 gnutls_authz_send_x509_attr_cert_url (gnutls_session_t session
,
527 gnutls_mac_algorithm_t hash_type
,
530 return add_data (session
, url
, urllen
, GNUTLS_AUTHZ_X509_ATTR_CERT_URL
,
535 * gnutls_authz_send_saml_assertion_url:
536 * @session: is a #gnutls_session_t structure.
537 * @url: buffer with a URL pointing to a SAML assertion.
538 * @urllen: length of buffer.
539 * @hash_type: type of hash in @hash.
540 * @hash: buffer with hash of URL target.
542 * Send a URL to a SAML assertion as authorization data, including a
543 * hash used to make sure the retrieved data was the intended data.
544 * This function may only be called inside a @send_callback set by
545 * gnutls_authz_enable().
547 * Returns: Returns 0 on success, or an error code on failures. If
548 * the supplied data was too long (the authorization extension only
549 * support 64kb large URLs), %GNUTLS_E_INVALID_REQUEST is returned.
552 gnutls_authz_send_saml_assertion_url (gnutls_session_t session
,
555 gnutls_mac_algorithm_t hash_type
,
558 return add_data (session
, url
, urllen
, GNUTLS_AUTHZ_X509_ATTR_CERT_URL
,
563 * gnutls_authz_enable:
564 * @session: is a #gnutls_session_t structure.
565 * @client_formats: zero-terminated list of
566 * #gnutls_authz_data_format_type_t elements with authorization
568 * @server_formats: zero-terminated list of
569 * #gnutls_authz_data_format_type_t elements with authorization
571 * @recv_callback: your callback function which will receive
572 * authz information when it is received.
573 * @send_callback: your callback function which is responsible for
574 * generating authorization data to send.
576 * Indicate willingness to send and receive authorization data, and
579 * For clients, @client_formats indicate which formats the client is
580 * willing to send, and @server_formats indicate which formats the
581 * client can receive.
583 * For servers, @client_formats indicate which formats the server is
584 * willing to accept from the client, and @server_formats indicate
585 * which formats the server is willing to send. Before the list is
586 * sent to the client, the formats which the client do not support are
587 * removed. If no supported formats remains, either or both of the
588 * extensions will not be sent.
590 * The @send_callback is invoked during the handshake if negotiation
591 * of the authorization extension was successful. The function
594 * int (*gnutls_authz_send_callback_func) (gnutls_session_t @session,
595 * const int *@client_formats, const int *@server_formats);
597 * The @client_format contains a list of successfully negotiated
598 * formats which the client may send data for to the server. The
599 * @server_formats contains a list of successfully neogitated formats
600 * which the server may send data for to the client. The callback is
601 * supposed to invoke gnutls_authz_send_x509_attr_cert(),
602 * gnutls_authz_send_saml_assertion(),
603 * gnutls_authz_send_x509_attr_cert_url(), or
604 * gnutls_authz_send_saml_assertion_url() for the data it wishes to
605 * send, passing along the @session parameter, and the data. The
606 * @client_format function should return 0 on success, or an error
607 * code, which may be used to abort the handshake on failures.
609 * The @recv_callback is invoked during the handshake when
610 * authorization data is received. The prototype of the callback
613 * int (*gnutls_authz_recv_callback_func) (gnutls_session_t session,
614 * const char *authz_formats, gnutls_datum_t *datums);
616 * The @authz_formats contains a list of formats for which data where
617 * received. The data for each format is stored in the @datums array,
618 * where the data associated with the @authz_formats[0] format is
619 * stored in @datums[0]. The function should return 0 on success, but
620 * may return an error, which may cause the handshake to abort.
622 * Note that there is no guarantee that @send_callback or
623 * @recv_callback is invoked just because gnutls_authz_enable was
624 * invoked. Whether the callbacks are invoked depend on whether
625 * negotiation of the extension succeeds. Therefor, if verification
626 * of authorization data is done by the @recv_callback, care should be
627 * made that if the callback is never invoked, it is not interpretetd
628 * as successful authorization verification. It is suggested to add
629 * some logic check whether authorization data was successfully
630 * verified after the call to gnutls_handshake(). That logic could
631 * shut down the connection if the authorization data is insufficient.
633 * This function have no effect if it is called during a handshake.
636 gnutls_authz_enable (gnutls_session_t session
,
637 const int *client_formats
,
638 const int *server_formats
,
639 gnutls_authz_recv_callback_func recv_callback
,
640 gnutls_authz_send_callback_func send_callback
)
642 int *session_client_formats
=
643 session
->security_parameters
.extensions
.authz_client_formats
;
644 int *session_server_formats
=
645 session
->security_parameters
.extensions
.authz_server_formats
;
648 if (session
->internals
.handshake_state
!= STATE0
)
651 for (i
= 0; client_formats
[i
]; i
++)
652 if (i
< MAX_AUTHZ_FORMATS
)
653 session_client_formats
[i
] = client_formats
[i
];
654 if (i
< MAX_AUTHZ_FORMATS
)
655 session_client_formats
[i
] = 0;
657 session_client_formats
[MAX_AUTHZ_FORMATS
] = 0;
659 for (i
= 0; server_formats
[i
]; i
++)
660 if (i
< MAX_AUTHZ_FORMATS
)
661 session_server_formats
[i
] = server_formats
[i
];
662 if (i
< MAX_AUTHZ_FORMATS
)
663 session_server_formats
[i
] = 0;
665 session_server_formats
[MAX_AUTHZ_FORMATS
] = 0;
667 session
->security_parameters
.extensions
.authz_recv_callback
= recv_callback
;
668 session
->security_parameters
.extensions
.authz_send_callback
= send_callback
;