/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/*
* SMBFS GSS-API
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <netdb.h>
#include <gssapi/gssapi_ext.h>
#include <smbsrv/libsmbns.h>
#include "smbfs_lib.h"
#include "smbfs_private.h"
typedef struct smbfs_session_key_t {
/*
* smbfs_gss_error
*
* Display GSS error message from error code. This routine is used to display
* the mechanism independent and mechanism specific error messages for GSS
* routines. The major status error code is the mechanism independent error
* code and the minor status error code is the mechanism specific error code.
*/
static void
{
return;
return;
DPRINT("SMBFS (gss-api): %s: GSS major status error: %s",
return;
return;
DPRINT("SMBFS (gss-api): %s: GSS minor status error: %s",
}
/*
* smbfs_gss_get_targetname
*
* Returns a GSS-API compatible target name for the host target.
* The format of the target name is
* <SAM account>@<REALM>
* where SAM account is in the format of <NetBIOS name>$
* NetBIOS name is a hostname truncated at first '.' or 15 bytes, whichever
* occurs first, and converted to uppercase.
*
* The function return 0 on success and -1 on failure.
*/
static int
{
if (*drealm == '\0')
return (-1);
*p = '\0';
if (buflen >= NETBIOS_NAME_SZ)
(void) smb_strupr(buf);
return (-1);
target_name)) != GSS_S_COMPLETE) {
return (-1);
}
return (0);
}
/*
* smbfs_gss_tok2mbdata
*
* Converts input GSS token to output mbdata buffer format for sending to SMB
* server.
*/
static int
{
mbuf_t *m;
int length;
return (-1);
if (m == NULL)
return (-1);
return (0);
}
/*
* smbfs_gss_mbdata2tok
*
* Converts received mbdata buffer format to GSS token for processing by SMB
* client.
*/
static int
{
mbuf_t *m;
return (-1);
return (-1);
return (0);
}
/*
* smbfs_gss_release_oid
*
* Release the gss_OID if it was allocated.
*/
static void
{
return;
}
/*
* smbfs_gss_cleanup
*
* Cleans up the GSS-API objects allocated.
*/
static void
{
if (*context != GSS_C_NO_CONTEXT)
(void) gss_delete_sec_context(min_stat,
if (*cred != GSS_C_NO_CREDENTIAL)
}
/*
* smbfs_gss_inquire_cred
*
* This function is used to inquire the validity of passed credentials.
* Sets errno to ETIME, if the lifetime of the credentials expired.
*
* To account for clock sync. issues between Solaris Client and KDC,
* we will return ETIME within 5 minutes of the reported lifetime. This will
* result in re-acquiring of Kerberos credentials.
*/
static int
{
if (maj_stat != GSS_S_COMPLETE) {
return (-1);
}
if (maj_stat != GSS_S_COMPLETE) {
return (-1);
}
DPRINT("Cred=\"%.*s\", lifetime %d\n",
if (lifetime <= 300) {
return (-1);
}
return (0);
}
/*
* smbfs_gss_cred_import_name
*
* This function converts the specified credential principal into
* internal gss_import_name format.
*
* Returns 0 on success, -1 on failure.
*/
static int
{
char *princ;
int rc = 0;
return (-1);
return (-1);
} else {
return (-1);
}
if (maj_stat != GSS_S_COMPLETE) {
rc = -1;
}
return (rc);
}
/*
* smbfs_gss_acquire_cred
*
* SMBFS client needs to acquire the credentials of the user in order to
* initiate security context establishment. Acquiring credentials of a user
* will be performed by using the gss_acquire_cred() function of GSS-API.
*
* For outbound DC request, the credential cache of SMB server
* will be used to query for client credentials.
*
* For all other type of requests, the credential cache will be queried in the
* following order,
* - Default Kerberos client credential cache.
* - SMB client credential cache.
*
* Return 0 on success, -1 on failure.
* Sets errno to ETIME, if the lifetime of the credentials expired.
*/
static int
{
int i, rc = 0;
return (-1);
for (i = SMBFS_CCACHE_SRV_SYS;
i < SMBFS_CCACHE_SRV_MAX; i++) {
break;
if (maj_stat == GSS_S_COMPLETE)
break;
}
} else {
for (i = SMBFS_CCACHE_CLNT_DEFAULT;
i < SMBFS_CCACHE_CLNT_MAX; i++) {
break;
if (maj_stat == GSS_S_COMPLETE)
break;
}
}
min_stat);
return (-1);
}
errno = 0;
if (smbfs_gss_inquire_cred(cred) != 0) {
return (-1);
}
return (0);
}
/*
* smbfs_gss_is_cred
*
* This function returns B_TRUE if credentials can be obtained for the client
* context. If the existing credentials have expired, attempt will be made to
* renew the expired credentials.
*
* Returns B_FALSE on error.
*/
{
} else {
}
if (cred != GSS_C_NO_CREDENTIAL)
return (ret);
}
/*
* smbfs_session_key_create
*
* Allocate and create a session key.
*/
static int
{
return (-1);
return (0);
}
/*
* smbfs_session_key_destroy
*
* Destroys a session key.
*/
static void
{
}
}
/*
* smbfs_extract_ssnkey
*
* Extracts session key from KRB_AP_REP message using
* gss_inquire_sec_context_by_oid API.
*
* The data set returned by the GSS inquiry function is composed
* of two elements:
* 1st element: the actual session key value and length
* 2nd element: the associated key encryption type by OID.
*/
static int
{
int rc;
if (session_key == NULL)
return (-1);
return (-1);
}
if (data_set == GSS_C_NO_BUFFER_SET ||
DPRINT("no session key");
return (-1);
}
DPRINT("unable to create smbfs session key");
return (rc);
}
/*
* smbfs_gss_setkey_signing
*
* Called after successful authentication.
* Setup the session key and MAC key for signing.
*
* [MS-KILE] section 3.1.1.2
*
* The subkey in the EncAPRepPart of the KRB_AP_REP message SHOULD be used as
* the session key when MutualAuthentication is requested. (The KRB_AP_REP
* message and its fields are defined in section 5.5.2 of [RFC4120].) When DES
* and RC4 are used, the implementation is as described in [RFC1964]. With DES
* and RC4, the subkey in the KRB_AP_REQ message can be used as the session key,
* as it is the same as the subkey in KRB_AP_REP message; however when AES is
* used (see [RFC4121]), the subkeys are different and the subkey in the
* KRB_AP_REP SHOULD be used. (The KRB_AP_REQ message is defined in section
* 5.5.1 of [RFC4120]).
*/
static int
{
int len;
return (-1);
ctx->ct_mackeylen = 0;
return (-1);
}
ctx->ct_mackeylen);
}
return (0);
}
/*
* smbfs_gss_setflags_signing
*
* Setup flags to do signing.
*/
static void
{
ctx->ct_hflags2 |=
}
}
}
/*
* smbfs_gss_init_sec
*
* Perform GSS context-establishement with the SMB server.
*
* [MS-SMB] If the capabilities returned in the SMB_COM_NEGOTIATE response
* include CAP_EXTENDED_SECURITY, the client MUST set the GSSNegotiateToken to
* the value returned in the SecurityBlob field in the SMB_COM_NEGOTIATE server
* response. The client MUST initiate the GSS authentication protocol via
* the gss_init_sec_context() interface, as specified in [RFC2743], by passing
* in the input GSSNegotiateToken.
*
* In our case, input_tok contains the value of the Security blob field
* returned by the SMB server in SMB_COM_NEGOTIATE response. This input_tok
* is assigned to token_ptr on the first pass, which is then passed to
* gss_init_sec_context() function.
*
* The input GSS mechanism OID (mech_OID) is set to the OID of the SPNEGO
* mechanism in Solaris.
*
* Every token generated by gss_init_sec_context() is stored in send_tok
* which is then transmitted to the server; every received token is stored in
* recv_tok, which token_ptr is then set to, to be processed by the next call
* to gss_init_sec_context.
*
* GSS-API guarantees that send_tok's length will be non-zero
* if and only if the server is expecting another token from us,
* and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if
* and only if the server has another token to send us.
*
* Returns 0 on success, EAUTH on failure.
* If the first request has already successfully send, then failure to process
* subsequest requests will return EBADE.
*/
int
{
int recv_len = 0;
int rc = 0;
char *str;
return (EAUTH);
&trgt_name) != 0)
return (EAUTH);
return (EAUTH);
}
if (maj_stat != GSS_S_COMPLETE) {
return (EAUTH);
}
return (EAUTH);
}
do {
0,
NULL,
&send_tok,
NULL);
if (recv_len > 0)
if (maj_stat != GSS_S_COMPLETE &&
}
}
}
}
if (maj_stat == GSS_S_CONTINUE_NEEDED) {
}
}
} while (maj_stat == GSS_S_CONTINUE_NEEDED);
return (rc);
}