The GSSAPI is a cryptographic API allowing the authentication of clients and servers, and provides integrity and privacy protection of messages. There is often already a GSSAPI library installed on system level (e.g. MIT Kerberos or Heimdal), and this library provides such services in a portable way. There are many crypto mechanisms specified for GSSAPI, and worthwhile to mention are:
Netsys_gssapi.GSSAPI
as a first-class module. We don't want to go here
into detail how the GSSAPI functions are called, but instead explain how
you can use the GSSAPI together with the network protocol implementations
in OCamlnet.
OPAM users: Note that the OPAM package for OCamlnet does not
build the GSSAPI module by default. The trigger for this is the presence
of the conf-gssapi
OPAM package, i.e. do opam install conf-gssapi
to rebuild with GSSAPI.
So far, OCamlnet only supports the base interface as speficied in the RFCs 2743 and 2744. There are interesting extensions, in particular for setting session parameters and for getting credentials with passwords. These will be added in future OCamlnet releases.
The GSSAPI on Windows: Microsoft "sort of" implements the GSSAPI. However, instead of using the C API specified by the IETF Windows goes its own proprietary way. At this point, OCamlnet does not support the SSPI API Windows provides, as it would exceed the resources of the developers. Note that there is also an edition of MIT Kerberos for Windows, and this could be an easy path to get full GSSAPI support. It has not yet been tested, though (any feedback on this is welcome).
Getting acces to the system-level GSSAPI library is simple:
netgss-system
findlib package to your buildNetgss.System
which is an implementation of Netsys_gssapi.GSSAPI
:
let gssapi = (module Netgss.System)
Netgss.System
are actually wrappers around
the system-level GSSAPI.
What you can do with the system-level GSSAPI: The common way of using it is that by calling the GSSAPI with default values you can log in to any network services secured with it. For example, if there is Kerberos deployed in the local network, the GSSAPI would then make a Kerberos login. This is probably the most likely scenario. Of course, there could also be some other network authentication service, and the GSSAPI could be configured to use this other service for doing network logins. The point here is that the client program needs not to know which authentication mechanism is deployed in the network.
When used in this way, it is common that the client program requesting access to secured services need not know the credentials (i.e. access tickets or passwords). The system-level GSSAPI knows how to get the credentials and how to use them for a service login. Of course, this also means you can only login as system user (i.e. if you are "tim" on your local machine, you'll also be "tim" when logging in to a network service).
It is outside the scope of this text how you can configure the system-level GSSAPI so that the default login method is the default network login.
The system-level GSSAPI may also make other mechanisms available to client programs. In this case, you need to request specific authentication mechanisms and not just the default. Often, it is also required to set these mechanisms up in a special way (e.g. provide a database with passwords). The GSSAPI as such does not address these configuration issues. It only includes functions for using a mechanism in a protocol.
As we are only using the GSSAPI functions from OCaml, it is also possible
to define a GSSAPI module directly in OCaml instead of wrapping C functions.
Within OCamlnet, there is so far only Netmech_scram_gssapi
following this
path. The nice aspect of this approach is that we can configure the security
mechanism directly, without having to edit files or using obscure helper
functions, e.g.
let gssapi =
Netmech_scram_gssapi.scran_gss_api
~client_key_ring
(Netmech_scram.profile `GSSAPI `Sha1)
Here, the client_key_ring
contains already the client credentials, which
then need not to be passed through the GSSAPI functions.
The GSSAPI is passed as first-class module to the protocol interpreter.
In the above code, the variable gssapi
is already such a module.
At the time of writing this text, these interpreters support the GSSAPI:
Rpc_client
Rpc_server
Nethttp_client
with the help of Netmech_spnego_http
Netftp_client
and Netftp_fs
Netpop
Netsmtp
gssapi
variable can be directly passed to the
protocol interpreter, but even in this case some extra configuration
is sometimes needed. An example of this is Netftp_fs
:
let fs =
Netftp_fs.ftp_fs
...
~gssapi_provider:gssapi
...
"ftp://user@host/path"
The protocol interpreters assume then reasonable defaults: they select the default authentication mechanism, and assume the default identity. This is usually the right thing when you do a system-level network login.
However, even in this case there are a few things that may need tuning:
Netftp_fs
and some other interpreters you can also pass a
gssap_config
argument, which is an instance of Netsys_gssapi.client_config
.
For example, to enforce encryption, you'd do:
let gssapi_config =
Netsys_gssapi.create_client_config
~privacy:`Required
()
let fs =
Netftp_fs.ftp_fs
...
~gssapi_provider:gssapi
~gssapi_config
...
"ftp://user@host/path"
(By default, this is set to `If_possible
.)
SASL is a authentication framework that is very similar to GSSAPI. The main difference is that SASL is directly defined as protocol, whereas GSSAPI takes the detour and first defines a protocol API, and then describes how to use this API in a concrete protocol. Because of the absence of the API, SASL is a lot simpler, and also has fewer features. So far, SASL is mainly used for challenge/response password authentication whereas GSSAPI targets complicated authentication suites such as Kerberos.
Nevertheless, both approaches are similar enough that it is possible to encapsulate any GSSAPI authentication exchange as SASL exchange. This is called "bridging". This way it is possible to use GSSAPI for protocols that only have a SASL option. Note that there is no standard for the opposite direction, as SASL is commonly seen as the weaker standard, and there was so far not enough support for wrapping SASL mechanisms so that they are usable from GSSAPI (but note that there are mechanisms which are defined for both SASL and GSSAPI, e.g. SCRAM).
In Ocamlnet, there are two bridging methods:
Netmech_krb5_sasl.Krb5_gs1
is the older method now called "GS1".
It is only used for Kerberos 5.Netmech_gs2_sasl
is the newer method "GS2". It can be used for
any mechanism, but needs some configuration. For Kerberos, there is
a ready-to-use configuration, and Netmech_krb5_sasl.Krb5_gs2
is
the new way to wrap Kerberos for SASL.Netpop
provides a function for authenticating
a client with an existing connection to a server. For a normal
password login using SASL the Netpop.authenticate
call could look like
Netpop.authenticate
~sasl_mechs:[ (module Netmech_scram_sasl.SCRAM_SHA1);
(module Netmech_digest_sasl.DIGEST_MD5);
]
~user:"tom"
~creds:[ "password", "sEcReT", [] ]
client
For GSSAPI, you mainly need to wrap the GSSAPI provider with some additional configuration. In particular, you need to pass the name of the service that is addressed. For POP, this name normally is the string "pop@" followed by the fully-qualified host name.
module K = Netmech_krb5_sasl.Krb5_gs1(Netgss.System)
Netpop.authenticate
~sasl_mechs:[ (module K) ]
~sasl_params:[ "gssapi-acceptor", "pop@mail.provider.com", false ]
~user:"any"
~creds:[]
client
Note that the user name is ignored in this case, and that there are no credentials.
As an API, this authentication framework defines a handful of functions. Only four of these are the really important ones doing all the work whereas the remaining ones play only an assisting role:
init_sec_context
: This function is repeatedly called by the client
for authentication. After the first call, it generates a token, which
needs to be sent to the server. The server responds with a response
token, which is fed ot the next invocation of init_sec_context
.
This loop goes on until init_sec_context
signals that the authentication
is successful (or failed). In the positive case, the caller gets an
initialized security context.accept_sec_context
: This function is the counterpart in the server.
Tokens coming from the client are passed as arguments to
accept_sec_context
, and the output of the function includes the
response tokens. If the authentication is successful, the server
also gets a security context.wrap
: After the authentication, this function can be called to
sign and/or encrypt a message.unwrap
: This is the function to check the signature and/or to decrypt
the message.init_sec_context
and accept_sec_context
. Once the
authentication is done, the functions wrap
and unwrap
can be used
to securely exchange messages.
Often a protocol utilizes only the authentication service from GSSAPI and does not wrap any messages (e.g. HTTP or the bridges for SASL). In this case, it is highly recommended to use another security layer such as TLS to protect the data exchange. Other protocols, however, securely wrap messages and need not the help of TLS. An excample of the latter is FTP.
The GSSAPI identifies authentication mechanisms by OIDs. (If you did not
yet encounter them, OIDs are a standardized way of naming "objects" that
often configure network protocols. OIDs are sequences of integers. There
is some support in Netoid
.) Examples:
[| 1;2;840;113554;1;2;2 |]
is the OID of Kerberos [| 1;3;6;1;5;5;14 |]
is the OID of SCRAMint array
.
The "principal" is the identity of a client or server with respect to the authentication protocol. For clients, this is usually a user name, and for servers a service name. The GSSAPI defines a few common ways of naming principals:
NT_USER_NAME
: This is the user name as a simple unstructured string,
e.g. "tim".NT_MACHINE_UID_NAME
: This is the numeric user name as a binary number
(big endian)NT_STRING_UID_NAME
. The numeric user name as a decimal numberNT_HOSTNAME_SERVICE
: This name identifies the service by combining
a service name like "imap" or "ftp" with the hostname: "service\@host"
NT_KRB5_PRINCIPAL_NAME
: This name type follows the Kerberos rules,
e.g. "tim@REALM" for users or "ftp/hostname@REALM" for services.
Netsys_gssapi.nt_user_name
= [| 1;2;840;113554;1;2;1;1 |]
Netsys_gssapi.nt_machine_uid_name
= [|1;2;840;113554;1;2;1;2 |]
Netsys_gssapi.nt_string_uid_name
= [|1;2;840;113554;1;2;1;3 |]
Netsys_gssapi.nt_hostbased_service
= [| 1;3;6;1;5;6;2 |]
Netsys_gssapi.nt_krb5_principal_name
= [| 1;2;840;113554;1;2;2;1 |]
string * Netoid.oid
For example, look at the target_name
argument of
Netsys_gssapi.create_client_config
.
There are also a few helper functions:
Netsys_gssapi.parse_hostbased_service
for getting the service name
and the host name of an NT_HOSTNAME_SERVICE
Netgssapi_support.parse_kerberos_name
for getting the components
of a Kerberos principalSo far there is no good support for:
credential
type. This primarily
means that it is unspecified how a user proves its identity. For the
system-level GSSAPI this means you can only authenticate as the default
principal, i.e. as the user you already be on the system level. It is
not possible to log in as another user.