wmi-1.3.16 from opsview.com
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
#################################
|
||||
# Start SUBSYSTEM CREDENTIALS
|
||||
[SUBSYSTEM::CREDENTIALS]
|
||||
PUBLIC_PROTO_HEADER = credentials_proto.h
|
||||
PUBLIC_HEADERS = credentials.h
|
||||
OBJ_FILES = credentials.o \
|
||||
credentials_files.o \
|
||||
credentials_ntlm.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
LIBCLI_AUTH SECRETS LIBCRYPTO KERBEROS
|
||||
PRIVATE_DEPENDENCIES = CREDENTIALS_KRB5
|
||||
# End SUBSYSTEM CREDENTIALS
|
||||
#################################
|
||||
|
||||
#################################
|
||||
# Start SUBSYSTEM CREDENTIALS
|
||||
[SUBSYSTEM::CREDENTIALS_KRB5]
|
||||
PUBLIC_PROTO_HEADER = credentials_krb5_proto.h
|
||||
PUBLIC_HEADERS = credentials_krb5.h
|
||||
OBJ_FILES = credentials_krb5.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
HEIMDAL_GSSAPI
|
||||
# End SUBSYSTEM CREDENTIALS
|
||||
#################################
|
||||
@@ -0,0 +1,717 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
User credentials handling
|
||||
|
||||
Copyright (C) Jelmer Vernooij 2005
|
||||
Copyright (C) Tim Potter 2001
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/credentials/credentials_krb5.h"
|
||||
#include "libcli/auth/libcli_auth.h"
|
||||
|
||||
/**
|
||||
* Create a new credentials structure
|
||||
* @param mem_ctx TALLOC_CTX parent for credentials structure
|
||||
*/
|
||||
struct cli_credentials *cli_credentials_init(TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct cli_credentials *cred = talloc(mem_ctx, struct cli_credentials);
|
||||
if (!cred) {
|
||||
return cred;
|
||||
}
|
||||
|
||||
cred->netlogon_creds = NULL;
|
||||
cred->machine_account_pending = False;
|
||||
cred->workstation_obtained = CRED_UNINITIALISED;
|
||||
cred->username_obtained = CRED_UNINITIALISED;
|
||||
cred->password_obtained = CRED_UNINITIALISED;
|
||||
cred->domain_obtained = CRED_UNINITIALISED;
|
||||
cred->realm_obtained = CRED_UNINITIALISED;
|
||||
cred->ccache_obtained = CRED_UNINITIALISED;
|
||||
cred->client_gss_creds_obtained = CRED_UNINITIALISED;
|
||||
cred->server_gss_creds_obtained = CRED_UNINITIALISED;
|
||||
cred->keytab_obtained = CRED_UNINITIALISED;
|
||||
cred->principal_obtained = CRED_UNINITIALISED;
|
||||
|
||||
cred->old_password = NULL;
|
||||
cred->smb_krb5_context = NULL;
|
||||
cred->salt_principal = NULL;
|
||||
cred->machine_account = False;
|
||||
|
||||
cred->bind_dn = NULL;
|
||||
|
||||
cred->tries = 3;
|
||||
cred->callback_running = False;
|
||||
|
||||
cli_credentials_set_kerberos_state(cred, CRED_AUTO_USE_KERBEROS);
|
||||
|
||||
return cred;
|
||||
}
|
||||
|
||||
void cli_credentials_set_kerberos_state(struct cli_credentials *creds,
|
||||
enum credentials_use_kerberos use_kerberos)
|
||||
{
|
||||
creds->use_kerberos = use_kerberos;
|
||||
}
|
||||
|
||||
enum credentials_use_kerberos cli_credentials_get_kerberos_state(struct cli_credentials *creds)
|
||||
{
|
||||
return creds->use_kerberos;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the username for this credentials context.
|
||||
* @param cred credentials context
|
||||
* @retval The username set on this context.
|
||||
* @note Return value will never be NULL except by programmer error.
|
||||
*/
|
||||
const char *cli_credentials_get_username(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
if (cred->username_obtained == CRED_CALLBACK &&
|
||||
!cred->callback_running) {
|
||||
cred->callback_running = True;
|
||||
cred->username = cred->username_cb(cred);
|
||||
cred->callback_running = False;
|
||||
cred->username_obtained = CRED_SPECIFIED;
|
||||
}
|
||||
|
||||
return cred->username;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_username(struct cli_credentials *cred,
|
||||
const char *val, enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->username_obtained) {
|
||||
cred->username = talloc_strdup(cred, val);
|
||||
cred->username_obtained = obtained;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_username_callback(struct cli_credentials *cred,
|
||||
const char *(*username_cb) (struct cli_credentials *))
|
||||
{
|
||||
if (cred->username_obtained < CRED_CALLBACK) {
|
||||
cred->username_cb = username_cb;
|
||||
cred->username_obtained = CRED_CALLBACK;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_bind_dn(struct cli_credentials *cred,
|
||||
const char *bind_dn)
|
||||
{
|
||||
cred->bind_dn = talloc_strdup(cred, bind_dn);
|
||||
return True;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the BIND DN for this credentials context.
|
||||
* @param cred credentials context
|
||||
* @retval The username set on this context.
|
||||
* @note Return value will be NULL if not specified explictly
|
||||
*/
|
||||
const char *cli_credentials_get_bind_dn(struct cli_credentials *cred)
|
||||
{
|
||||
return cred->bind_dn;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the client principal for this credentials context.
|
||||
* @param cred credentials context
|
||||
* @retval The username set on this context.
|
||||
* @note Return value will never be NULL except by programmer error.
|
||||
*/
|
||||
const char *cli_credentials_get_principal(struct cli_credentials *cred, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
if (cred->principal_obtained == CRED_CALLBACK &&
|
||||
!cred->callback_running) {
|
||||
cred->callback_running = True;
|
||||
cred->principal = cred->principal_cb(cred);
|
||||
cred->callback_running = False;
|
||||
cred->principal_obtained = CRED_SPECIFIED;
|
||||
}
|
||||
|
||||
if (cred->principal_obtained < cred->username_obtained) {
|
||||
if (cred->domain_obtained > cred->realm_obtained) {
|
||||
return talloc_asprintf(mem_ctx, "%s@%s",
|
||||
cli_credentials_get_username(cred),
|
||||
cli_credentials_get_domain(cred));
|
||||
} else {
|
||||
return talloc_asprintf(mem_ctx, "%s@%s",
|
||||
cli_credentials_get_username(cred),
|
||||
cli_credentials_get_realm(cred));
|
||||
}
|
||||
}
|
||||
return talloc_reference(mem_ctx, cred->principal);
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_principal(struct cli_credentials *cred,
|
||||
const char *val,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->principal_obtained) {
|
||||
cred->principal = talloc_strdup(cred, val);
|
||||
cred->principal_obtained = obtained;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Set a callback to get the principal. This could be a popup dialog,
|
||||
* a terminal prompt or similar. */
|
||||
|
||||
BOOL cli_credentials_set_principal_callback(struct cli_credentials *cred,
|
||||
const char *(*principal_cb) (struct cli_credentials *))
|
||||
{
|
||||
if (cred->principal_obtained < CRED_CALLBACK) {
|
||||
cred->principal_cb = principal_cb;
|
||||
cred->principal_obtained = CRED_CALLBACK;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Some of our tools are 'anonymous by default'. This is a single
|
||||
* function to determine if authentication has been explicitly
|
||||
* requested */
|
||||
|
||||
BOOL cli_credentials_authentication_requested(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->bind_dn) {
|
||||
return True;
|
||||
}
|
||||
|
||||
if (cli_credentials_is_anonymous(cred)){
|
||||
return False;
|
||||
}
|
||||
|
||||
if (cred->principal_obtained >= CRED_SPECIFIED) {
|
||||
return True;
|
||||
}
|
||||
if (cred->username_obtained >= CRED_SPECIFIED) {
|
||||
return True;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the password for this credentials context.
|
||||
* @param cred credentials context
|
||||
* @retval If set, the cleartext password, otherwise NULL
|
||||
*/
|
||||
const char *cli_credentials_get_password(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
if (cred->password_obtained == CRED_CALLBACK &&
|
||||
!cred->callback_running) {
|
||||
cred->callback_running = True;
|
||||
cred->password = cred->password_cb(cred);
|
||||
cred->callback_running = False;
|
||||
cred->password_obtained = CRED_CALLBACK_RESULT;
|
||||
}
|
||||
|
||||
return cred->password;
|
||||
}
|
||||
|
||||
/* Set a password on the credentials context, including an indication
|
||||
* of 'how' the password was obtained */
|
||||
|
||||
BOOL cli_credentials_set_password(struct cli_credentials *cred,
|
||||
const char *val,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->password_obtained) {
|
||||
cred->password = talloc_strdup(cred, val);
|
||||
cred->password_obtained = obtained;
|
||||
|
||||
cred->nt_hash = NULL;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_password_callback(struct cli_credentials *cred,
|
||||
const char *(*password_cb) (struct cli_credentials *))
|
||||
{
|
||||
if (cred->password_obtained < CRED_CALLBACK) {
|
||||
cred->password_cb = password_cb;
|
||||
cred->password_obtained = CRED_CALLBACK;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the 'old' password for this credentials context (used for join accounts).
|
||||
* @param cred credentials context
|
||||
* @retval If set, the cleartext password, otherwise NULL
|
||||
*/
|
||||
const char *cli_credentials_get_old_password(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
return cred->old_password;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_old_password(struct cli_credentials *cred,
|
||||
const char *val,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
cred->old_password = talloc_strdup(cred, val);
|
||||
return True;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the password, in the form MD4(unicode(password)) for this credentials context.
|
||||
*
|
||||
* Sometimes we only have this much of the password, while the rest of
|
||||
* the time this call avoids calling E_md4hash themselves.
|
||||
*
|
||||
* @param cred credentials context
|
||||
* @retval If set, the cleartext password, otherwise NULL
|
||||
*/
|
||||
const struct samr_Password *cli_credentials_get_nt_hash(struct cli_credentials *cred,
|
||||
TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
const char *password = cli_credentials_get_password(cred);
|
||||
|
||||
if (password) {
|
||||
struct samr_Password *nt_hash = talloc(mem_ctx, struct samr_Password);
|
||||
if (!nt_hash) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
E_md4hash(password, nt_hash->hash);
|
||||
|
||||
return nt_hash;
|
||||
} else {
|
||||
return cred->nt_hash;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_nt_hash(struct cli_credentials *cred,
|
||||
const struct samr_Password *nt_hash,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->password_obtained) {
|
||||
cli_credentials_set_password(cred, NULL, obtained);
|
||||
cred->nt_hash = talloc(cred, struct samr_Password);
|
||||
*cred->nt_hash = *nt_hash;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the 'short' or 'NetBIOS' domain for this credentials context.
|
||||
* @param cred credentials context
|
||||
* @retval The domain set on this context.
|
||||
* @note Return value will never be NULL except by programmer error.
|
||||
*/
|
||||
const char *cli_credentials_get_domain(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
if (cred->domain_obtained == CRED_CALLBACK &&
|
||||
!cred->callback_running) {
|
||||
cred->callback_running = True;
|
||||
cred->domain = cred->domain_cb(cred);
|
||||
cred->callback_running = False;
|
||||
cred->domain_obtained = CRED_SPECIFIED;
|
||||
}
|
||||
|
||||
return cred->domain;
|
||||
}
|
||||
|
||||
|
||||
BOOL cli_credentials_set_domain(struct cli_credentials *cred,
|
||||
const char *val,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->domain_obtained) {
|
||||
/* it is important that the domain be in upper case,
|
||||
* particularly for the sensitive NTLMv2
|
||||
* calculations */
|
||||
cred->domain = strupper_talloc(cred, val);
|
||||
cred->domain_obtained = obtained;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_domain_callback(struct cli_credentials *cred,
|
||||
const char *(*domain_cb) (struct cli_credentials *))
|
||||
{
|
||||
if (cred->domain_obtained < CRED_CALLBACK) {
|
||||
cred->domain_cb = domain_cb;
|
||||
cred->domain_obtained = CRED_CALLBACK;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the Kerberos realm for this credentials context.
|
||||
* @param cred credentials context
|
||||
* @retval The realm set on this context.
|
||||
* @note Return value will never be NULL except by programmer error.
|
||||
*/
|
||||
const char *cli_credentials_get_realm(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
if (cred->realm_obtained == CRED_CALLBACK &&
|
||||
!cred->callback_running) {
|
||||
cred->callback_running = True;
|
||||
cred->realm = cred->realm_cb(cred);
|
||||
cred->callback_running = False;
|
||||
cred->realm_obtained = CRED_SPECIFIED;
|
||||
}
|
||||
|
||||
return cred->realm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the realm for this credentials context, and force it to
|
||||
* uppercase for the sainity of our local kerberos libraries
|
||||
*/
|
||||
BOOL cli_credentials_set_realm(struct cli_credentials *cred,
|
||||
const char *val,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->realm_obtained) {
|
||||
cred->realm = strupper_talloc(cred, val);
|
||||
cred->realm_obtained = obtained;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_realm_callback(struct cli_credentials *cred,
|
||||
const char *(*realm_cb) (struct cli_credentials *))
|
||||
{
|
||||
if (cred->realm_obtained < CRED_CALLBACK) {
|
||||
cred->realm_cb = realm_cb;
|
||||
cred->realm_obtained = CRED_CALLBACK;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the 'short' or 'NetBIOS' workstation name for this credentials context.
|
||||
*
|
||||
* @param cred credentials context
|
||||
* @retval The workstation name set on this context.
|
||||
* @note Return value will never be NULL except by programmer error.
|
||||
*/
|
||||
const char *cli_credentials_get_workstation(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->workstation_obtained == CRED_CALLBACK &&
|
||||
!cred->callback_running) {
|
||||
cred->callback_running = True;
|
||||
cred->workstation = cred->workstation_cb(cred);
|
||||
cred->callback_running = False;
|
||||
cred->workstation_obtained = CRED_SPECIFIED;
|
||||
}
|
||||
|
||||
return cred->workstation;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_workstation(struct cli_credentials *cred,
|
||||
const char *val,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->workstation_obtained) {
|
||||
cred->workstation = talloc_strdup(cred, val);
|
||||
cred->workstation_obtained = obtained;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_workstation_callback(struct cli_credentials *cred,
|
||||
const char *(*workstation_cb) (struct cli_credentials *))
|
||||
{
|
||||
if (cred->workstation_obtained < CRED_CALLBACK) {
|
||||
cred->workstation_cb = workstation_cb;
|
||||
cred->workstation_obtained = CRED_CALLBACK;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string, typically obtained from a -U argument, parse it into domain, username, realm and password fields
|
||||
*
|
||||
* The format accepted is [domain\\]user[%password] or user[@realm][%password]
|
||||
*
|
||||
* @param credentials Credentials structure on which to set the password
|
||||
* @param data the string containing the username, password etc
|
||||
* @param obtained This enum describes how 'specified' this password is
|
||||
*/
|
||||
|
||||
void cli_credentials_parse_string(struct cli_credentials *credentials, const char *data, enum credentials_obtained obtained)
|
||||
{
|
||||
char *uname, *p;
|
||||
|
||||
if (strcmp("%",data) == 0) {
|
||||
cli_credentials_set_anonymous(credentials);
|
||||
return;
|
||||
}
|
||||
|
||||
uname = talloc_strdup(credentials, data);
|
||||
if ((p = strchr_m(uname,'%'))) {
|
||||
*p = 0;
|
||||
cli_credentials_set_password(credentials, p+1, obtained);
|
||||
}
|
||||
|
||||
if ((p = strchr_m(uname,'@'))) {
|
||||
cli_credentials_set_principal(credentials, uname, obtained);
|
||||
*p = 0;
|
||||
cli_credentials_set_realm(credentials, p+1, obtained);
|
||||
return;
|
||||
} else if ((p = strchr_m(uname,'\\')) || (p = strchr_m(uname, '/'))) {
|
||||
*p = 0;
|
||||
cli_credentials_set_domain(credentials, uname, obtained);
|
||||
uname = p+1;
|
||||
}
|
||||
cli_credentials_set_username(credentials, uname, obtained);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a a credentials structure, print it as a string
|
||||
*
|
||||
* The format output is [domain\\]user[%password] or user[@realm][%password]
|
||||
*
|
||||
* @param credentials Credentials structure on which to set the password
|
||||
* @param mem_ctx The memory context to place the result on
|
||||
*/
|
||||
|
||||
const char *cli_credentials_get_unparsed_name(struct cli_credentials *credentials, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
const char *bind_dn = cli_credentials_get_bind_dn(credentials);
|
||||
const char *domain;
|
||||
const char *username;
|
||||
const char *name;
|
||||
|
||||
if (bind_dn) {
|
||||
name = talloc_reference(mem_ctx, bind_dn);
|
||||
} else {
|
||||
cli_credentials_get_ntlm_username_domain(credentials, mem_ctx, &username, &domain);
|
||||
if (domain && domain[0]) {
|
||||
name = talloc_asprintf(mem_ctx, "%s\\%s",
|
||||
domain, username);
|
||||
} else {
|
||||
name = talloc_asprintf(mem_ctx, "%s",
|
||||
username);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies default values for domain, workstation and realm
|
||||
* from the smb.conf configuration file
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
*/
|
||||
void cli_credentials_set_conf(struct cli_credentials *cred)
|
||||
{
|
||||
cli_credentials_set_username(cred, "", CRED_UNINITIALISED);
|
||||
cli_credentials_set_domain(cred, lp_workgroup(), CRED_UNINITIALISED);
|
||||
cli_credentials_set_workstation(cred, lp_netbios_name(), CRED_UNINITIALISED);
|
||||
cli_credentials_set_realm(cred, lp_realm(), CRED_UNINITIALISED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guess defaults for credentials from environment variables,
|
||||
* and from the configuration file
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
*/
|
||||
void cli_credentials_guess(struct cli_credentials *cred)
|
||||
{
|
||||
char *p;
|
||||
|
||||
cli_credentials_set_conf(cred);
|
||||
|
||||
if (getenv("LOGNAME")) {
|
||||
cli_credentials_set_username(cred, getenv("LOGNAME"), CRED_GUESS_ENV);
|
||||
}
|
||||
|
||||
if (getenv("USER")) {
|
||||
cli_credentials_parse_string(cred, getenv("USER"), CRED_GUESS_ENV);
|
||||
if ((p = strchr_m(getenv("USER"),'%'))) {
|
||||
memset(p,0,strlen(cred->password));
|
||||
}
|
||||
}
|
||||
|
||||
if (getenv("DOMAIN")) {
|
||||
cli_credentials_set_domain(cred, getenv("DOMAIN"), CRED_GUESS_ENV);
|
||||
}
|
||||
|
||||
if (getenv("PASSWD")) {
|
||||
cli_credentials_set_password(cred, getenv("PASSWD"), CRED_GUESS_ENV);
|
||||
}
|
||||
|
||||
if (getenv("PASSWD_FD")) {
|
||||
cli_credentials_parse_password_fd(cred, atoi(getenv("PASSWD_FD")), CRED_GUESS_FILE);
|
||||
}
|
||||
|
||||
if (getenv("PASSWD_FILE")) {
|
||||
cli_credentials_parse_password_file(cred, getenv("PASSWD_FILE"), CRED_GUESS_FILE);
|
||||
}
|
||||
|
||||
if (cli_credentials_get_kerberos_state(cred) != CRED_DONT_USE_KERBEROS) {
|
||||
cli_credentials_set_ccache(cred, NULL, CRED_GUESS_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach NETLOGON credentials for use with SCHANNEL
|
||||
*/
|
||||
|
||||
void cli_credentials_set_netlogon_creds(struct cli_credentials *cred,
|
||||
struct creds_CredentialState *netlogon_creds)
|
||||
{
|
||||
cred->netlogon_creds = talloc_reference(cred, netlogon_creds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return attached NETLOGON credentials
|
||||
*/
|
||||
|
||||
struct creds_CredentialState *cli_credentials_get_netlogon_creds(struct cli_credentials *cred)
|
||||
{
|
||||
return cred->netlogon_creds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set NETLOGON secure channel type
|
||||
*/
|
||||
|
||||
void cli_credentials_set_secure_channel_type(struct cli_credentials *cred,
|
||||
enum netr_SchannelType secure_channel_type)
|
||||
{
|
||||
cred->secure_channel_type = secure_channel_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return NETLOGON secure chanel type
|
||||
*/
|
||||
|
||||
enum netr_SchannelType cli_credentials_get_secure_channel_type(struct cli_credentials *cred)
|
||||
{
|
||||
return cred->secure_channel_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in a credentials structure as the anonymous user
|
||||
*/
|
||||
void cli_credentials_set_anonymous(struct cli_credentials *cred)
|
||||
{
|
||||
cli_credentials_set_username(cred, "", CRED_SPECIFIED);
|
||||
cli_credentials_set_domain(cred, "", CRED_SPECIFIED);
|
||||
cli_credentials_set_password(cred, NULL, CRED_SPECIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe a credentials context as anonymous or authenticated
|
||||
* @retval True if anonymous, False if a username is specified
|
||||
*/
|
||||
|
||||
BOOL cli_credentials_is_anonymous(struct cli_credentials *cred)
|
||||
{
|
||||
const char *username;
|
||||
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
username = cli_credentials_get_username(cred);
|
||||
|
||||
/* Yes, it is deliberate that we die if we have a NULL pointer
|
||||
* here - anonymous is "", not NULL, which is 'never specified,
|
||||
* never guessed', ie programmer bug */
|
||||
if (!username[0]) {
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the current password for a credentials struct as wrong. This will
|
||||
* cause the password to be prompted again (if a callback is set).
|
||||
*
|
||||
* This will decrement the number of times the password can be tried.
|
||||
*
|
||||
* @retval whether the credentials struct is finished
|
||||
*/
|
||||
BOOL cli_credentials_wrong_password(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->password_obtained != CRED_CALLBACK_RESULT) {
|
||||
return False;
|
||||
}
|
||||
|
||||
cred->password_obtained = CRED_CALLBACK;
|
||||
|
||||
cred->tries--;
|
||||
|
||||
return (cred->tries > 0);
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
samba -- Unix SMB/CIFS implementation.
|
||||
|
||||
Client credentials structure
|
||||
|
||||
Copyright (C) Jelmer Vernooij 2004-2006
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#ifndef __CREDENTIALS_H__
|
||||
#define __CREDENTIALS_H__
|
||||
|
||||
#include "librpc/gen_ndr/misc.h"
|
||||
|
||||
struct ccache_container;
|
||||
|
||||
/* In order of priority */
|
||||
enum credentials_obtained {
|
||||
CRED_UNINITIALISED = 0, /* We don't even have a guess yet */
|
||||
CRED_GUESS_ENV, /* Current value should be used, which was guessed */
|
||||
CRED_CALLBACK, /* Callback should be used to obtain value */
|
||||
CRED_GUESS_FILE, /* A guess from a file (or file pointed at in env variable) */
|
||||
CRED_CALLBACK_RESULT, /* Value was obtained from a callback */
|
||||
CRED_SPECIFIED /* Was explicitly specified on the command-line */
|
||||
};
|
||||
|
||||
enum credentials_use_kerberos {
|
||||
CRED_AUTO_USE_KERBEROS = 0, /* Default, we try kerberos if available */
|
||||
CRED_DONT_USE_KERBEROS, /* Sometimes trying kerberos just does 'bad things', so don't */
|
||||
CRED_MUST_USE_KERBEROS /* Sometimes administrators are parinoid, so always do kerberos */
|
||||
};
|
||||
|
||||
#define CLI_CRED_NTLM2 0x01
|
||||
#define CLI_CRED_NTLMv2_AUTH 0x02
|
||||
#define CLI_CRED_LANMAN_AUTH 0x04
|
||||
#define CLI_CRED_NTLM_AUTH 0x08
|
||||
#define CLI_CRED_CLEAR_AUTH 0x10 /* TODO: Push cleartext auth with this flag */
|
||||
|
||||
struct cli_credentials {
|
||||
enum credentials_obtained workstation_obtained;
|
||||
enum credentials_obtained username_obtained;
|
||||
enum credentials_obtained password_obtained;
|
||||
enum credentials_obtained domain_obtained;
|
||||
enum credentials_obtained realm_obtained;
|
||||
enum credentials_obtained ccache_obtained;
|
||||
enum credentials_obtained client_gss_creds_obtained;
|
||||
enum credentials_obtained principal_obtained;
|
||||
enum credentials_obtained keytab_obtained;
|
||||
enum credentials_obtained server_gss_creds_obtained;
|
||||
|
||||
const char *workstation;
|
||||
const char *username;
|
||||
const char *password;
|
||||
const char *old_password;
|
||||
const char *domain;
|
||||
const char *realm;
|
||||
const char *principal;
|
||||
const char *salt_principal;
|
||||
|
||||
const char *bind_dn;
|
||||
|
||||
struct samr_Password *nt_hash;
|
||||
|
||||
struct ccache_container *ccache;
|
||||
struct gssapi_creds_container *client_gss_creds;
|
||||
struct keytab_container *keytab;
|
||||
struct gssapi_creds_container *server_gss_creds;
|
||||
|
||||
const char *(*workstation_cb) (struct cli_credentials *);
|
||||
const char *(*password_cb) (struct cli_credentials *);
|
||||
const char *(*username_cb) (struct cli_credentials *);
|
||||
const char *(*domain_cb) (struct cli_credentials *);
|
||||
const char *(*realm_cb) (struct cli_credentials *);
|
||||
const char *(*principal_cb) (struct cli_credentials *);
|
||||
|
||||
/* Private handle for the callback routines to use */
|
||||
void *priv_data;
|
||||
|
||||
struct creds_CredentialState *netlogon_creds;
|
||||
enum netr_SchannelType secure_channel_type;
|
||||
int kvno;
|
||||
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
|
||||
/* We are flagged to get machine account details from the
|
||||
* secrets.ldb when we are asked for a username or password */
|
||||
|
||||
BOOL machine_account_pending;
|
||||
|
||||
/* Is this a machine account? */
|
||||
BOOL machine_account;
|
||||
|
||||
/* Should we be trying to use kerberos? */
|
||||
enum credentials_use_kerberos use_kerberos;
|
||||
|
||||
/* Number of retries left before bailing out */
|
||||
int tries;
|
||||
|
||||
/* Whether any callback is currently running */
|
||||
BOOL callback_running;
|
||||
};
|
||||
|
||||
#include "auth/credentials/credentials_proto.h"
|
||||
|
||||
#endif /* __CREDENTIALS_H__ */
|
||||
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
User credentials handling (as regards on-disk files)
|
||||
|
||||
Copyright (C) Jelmer Vernooij 2005
|
||||
Copyright (C) Tim Potter 2001
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
|
||||
#include "param/secrets.h"
|
||||
#include "system/filesys.h"
|
||||
#include "db_wrap.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/credentials/credentials_krb5.h"
|
||||
|
||||
/**
|
||||
* Read a file descriptor, and parse it for a password (eg from a file or stdin)
|
||||
*
|
||||
* @param credentials Credentials structure on which to set the password
|
||||
* @param fd open file descriptor to read the password from
|
||||
* @param obtained This enum describes how 'specified' this password is
|
||||
*/
|
||||
|
||||
BOOL cli_credentials_parse_password_fd(struct cli_credentials *credentials,
|
||||
int fd, enum credentials_obtained obtained)
|
||||
{
|
||||
char *p;
|
||||
char pass[128];
|
||||
|
||||
for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
|
||||
p && p - pass < sizeof(pass);) {
|
||||
switch (read(fd, p, 1)) {
|
||||
case 1:
|
||||
if (*p != '\n' && *p != '\0') {
|
||||
*++p = '\0'; /* advance p, and null-terminate pass */
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case 0:
|
||||
if (p - pass) {
|
||||
*p = '\0'; /* null-terminate it, just in case... */
|
||||
p = NULL; /* then force the loop condition to become false */
|
||||
break;
|
||||
} else {
|
||||
fprintf(stderr, "Error reading password from file descriptor %d: %s\n", fd, "empty password\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Error reading password from file descriptor %d: %s\n",
|
||||
fd, strerror(errno));
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
cli_credentials_set_password(credentials, pass, obtained);
|
||||
return True;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a named file, and parse it for a password
|
||||
*
|
||||
* @param credentials Credentials structure on which to set the password
|
||||
* @param file a named file to read the password from
|
||||
* @param obtained This enum describes how 'specified' this password is
|
||||
*/
|
||||
|
||||
BOOL cli_credentials_parse_password_file(struct cli_credentials *credentials, const char *file, enum credentials_obtained obtained)
|
||||
{
|
||||
int fd = open(file, O_RDONLY, 0);
|
||||
BOOL ret;
|
||||
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
|
||||
file, strerror(errno));
|
||||
return False;
|
||||
}
|
||||
|
||||
ret = cli_credentials_parse_password_fd(credentials, fd, obtained);
|
||||
|
||||
close(fd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a named file, and parse it for username, domain, realm and password
|
||||
*
|
||||
* @param credentials Credentials structure on which to set the password
|
||||
* @param file a named file to read the details from
|
||||
* @param obtained This enum describes how 'specified' this password is
|
||||
*/
|
||||
|
||||
BOOL cli_credentials_parse_file(struct cli_credentials *cred, const char *file, enum credentials_obtained obtained)
|
||||
{
|
||||
uint16_t len = 0;
|
||||
char *ptr, *val, *param;
|
||||
char **lines;
|
||||
int i, numlines;
|
||||
|
||||
lines = file_lines_load(file, &numlines, NULL);
|
||||
|
||||
if (lines == NULL)
|
||||
{
|
||||
/* fail if we can't open the credentials file */
|
||||
d_printf("ERROR: Unable to open credentials file!\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
for (i = 0; i < numlines; i++) {
|
||||
len = strlen(lines[i]);
|
||||
|
||||
if (len == 0)
|
||||
continue;
|
||||
|
||||
/* break up the line into parameter & value.
|
||||
* will need to eat a little whitespace possibly */
|
||||
param = lines[i];
|
||||
if (!(ptr = strchr_m (lines[i], '=')))
|
||||
continue;
|
||||
|
||||
val = ptr+1;
|
||||
*ptr = '\0';
|
||||
|
||||
/* eat leading white space */
|
||||
while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
|
||||
val++;
|
||||
|
||||
if (strwicmp("password", param) == 0) {
|
||||
cli_credentials_set_password(cred, val, obtained);
|
||||
} else if (strwicmp("username", param) == 0) {
|
||||
cli_credentials_set_username(cred, val, obtained);
|
||||
} else if (strwicmp("domain", param) == 0) {
|
||||
cli_credentials_set_domain(cred, val, obtained);
|
||||
} else if (strwicmp("realm", param) == 0) {
|
||||
cli_credentials_set_realm(cred, val, obtained);
|
||||
}
|
||||
memset(lines[i], 0, len);
|
||||
}
|
||||
|
||||
talloc_free(lines);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fill in credentials for the machine trust account, from the secrets database.
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
* @retval NTSTATUS error detailing any failure
|
||||
*/
|
||||
NTSTATUS cli_credentials_set_secrets(struct cli_credentials *cred,
|
||||
const char *base,
|
||||
const char *filter)
|
||||
{
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
struct ldb_context *ldb;
|
||||
int ldb_ret;
|
||||
struct ldb_message **msgs;
|
||||
const char *attrs[] = {
|
||||
"secret",
|
||||
"priorSecret",
|
||||
"samAccountName",
|
||||
"flatname",
|
||||
"realm",
|
||||
"secureChannelType",
|
||||
"ntPwdHash",
|
||||
"msDS-KeyVersionNumber",
|
||||
"saltPrincipal",
|
||||
"privateKeytab",
|
||||
"krb5Keytab",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *machine_account;
|
||||
const char *password;
|
||||
const char *old_password;
|
||||
const char *domain;
|
||||
const char *realm;
|
||||
enum netr_SchannelType sct;
|
||||
const char *salt_principal;
|
||||
const char *keytab;
|
||||
|
||||
/* ok, we are going to get it now, don't recurse back here */
|
||||
cred->machine_account_pending = False;
|
||||
|
||||
/* some other parts of the system will key off this */
|
||||
cred->machine_account = True;
|
||||
|
||||
mem_ctx = talloc_named(cred, 0, "cli_credentials fetch machine password");
|
||||
|
||||
/* Local secrets are stored in secrets.ldb */
|
||||
ldb = secrets_db_connect(mem_ctx);
|
||||
if (!ldb) {
|
||||
/* set anonymous as the fallback, if the machine account won't work */
|
||||
cli_credentials_set_anonymous(cred);
|
||||
DEBUG(1, ("Could not open secrets.ldb\n"));
|
||||
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
||||
}
|
||||
|
||||
/* search for the secret record */
|
||||
ldb_ret = gendb_search(ldb,
|
||||
mem_ctx, ldb_dn_new(mem_ctx, ldb, base),
|
||||
&msgs, attrs,
|
||||
"%s", filter);
|
||||
if (ldb_ret == 0) {
|
||||
DEBUG(1, ("Could not find entry to match filter: %s\n",
|
||||
filter));
|
||||
/* set anonymous as the fallback, if the machine account won't work */
|
||||
cli_credentials_set_anonymous(cred);
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
||||
} else if (ldb_ret != 1) {
|
||||
DEBUG(1, ("Found more than one (%d) entry to match filter: %s\n",
|
||||
ldb_ret, filter));
|
||||
/* set anonymous as the fallback, if the machine account won't work */
|
||||
cli_credentials_set_anonymous(cred);
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
||||
}
|
||||
|
||||
password = ldb_msg_find_attr_as_string(msgs[0], "secret", NULL);
|
||||
old_password = ldb_msg_find_attr_as_string(msgs[0], "priorSecret", NULL);
|
||||
|
||||
machine_account = ldb_msg_find_attr_as_string(msgs[0], "samAccountName", NULL);
|
||||
|
||||
if (!machine_account) {
|
||||
DEBUG(1, ("Could not find 'samAccountName' in join record to domain: %s\n",
|
||||
cli_credentials_get_domain(cred)));
|
||||
/* set anonymous as the fallback, if the machine account won't work */
|
||||
cli_credentials_set_anonymous(cred);
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
||||
}
|
||||
|
||||
salt_principal = ldb_msg_find_attr_as_string(msgs[0], "saltPrincipal", NULL);
|
||||
cli_credentials_set_salt_principal(cred, salt_principal);
|
||||
|
||||
sct = ldb_msg_find_attr_as_int(msgs[0], "secureChannelType", 0);
|
||||
if (sct) {
|
||||
cli_credentials_set_secure_channel_type(cred, sct);
|
||||
}
|
||||
|
||||
if (!password) {
|
||||
const struct ldb_val *nt_password_hash = ldb_msg_find_ldb_val(msgs[0], "ntPwdHash");
|
||||
struct samr_Password hash;
|
||||
ZERO_STRUCT(hash);
|
||||
if (nt_password_hash) {
|
||||
memcpy(hash.hash, nt_password_hash->data,
|
||||
MIN(nt_password_hash->length, sizeof(hash.hash)));
|
||||
|
||||
cli_credentials_set_nt_hash(cred, &hash, CRED_SPECIFIED);
|
||||
} else {
|
||||
cli_credentials_set_password(cred, NULL, CRED_SPECIFIED);
|
||||
}
|
||||
} else {
|
||||
cli_credentials_set_password(cred, password, CRED_SPECIFIED);
|
||||
}
|
||||
|
||||
|
||||
domain = ldb_msg_find_attr_as_string(msgs[0], "flatname", NULL);
|
||||
if (domain) {
|
||||
cli_credentials_set_domain(cred, domain, CRED_SPECIFIED);
|
||||
}
|
||||
|
||||
realm = ldb_msg_find_attr_as_string(msgs[0], "realm", NULL);
|
||||
if (realm) {
|
||||
cli_credentials_set_realm(cred, realm, CRED_SPECIFIED);
|
||||
}
|
||||
|
||||
cli_credentials_set_username(cred, machine_account, CRED_SPECIFIED);
|
||||
|
||||
cli_credentials_set_kvno(cred, ldb_msg_find_attr_as_int(msgs[0], "msDS-KeyVersionNumber", 0));
|
||||
|
||||
/* If there was an external keytab specified by reference in
|
||||
* the LDB, then use this. Otherwise we will make one up
|
||||
* (chewing CPU time) from the password */
|
||||
keytab = ldb_msg_find_attr_as_string(msgs[0], "krb5Keytab", NULL);
|
||||
if (keytab) {
|
||||
cli_credentials_set_keytab_name(cred, keytab, CRED_SPECIFIED);
|
||||
} else {
|
||||
keytab = ldb_msg_find_attr_as_string(msgs[0], "privateKeytab", NULL);
|
||||
if (keytab) {
|
||||
keytab = talloc_asprintf(mem_ctx, "FILE:%s", private_path(mem_ctx, keytab));
|
||||
if (keytab) {
|
||||
cli_credentials_set_keytab_name(cred, keytab, CRED_SPECIFIED);
|
||||
}
|
||||
}
|
||||
}
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in credentials for the machine trust account, from the secrets database.
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
* @retval NTSTATUS error detailing any failure
|
||||
*/
|
||||
NTSTATUS cli_credentials_set_machine_account(struct cli_credentials *cred)
|
||||
{
|
||||
char *filter;
|
||||
/* Bleh, nasty recursion issues: We are setting a machine
|
||||
* account here, so we don't want the 'pending' flag around
|
||||
* any more */
|
||||
cred->machine_account_pending = False;
|
||||
filter = talloc_asprintf(cred, SECRETS_PRIMARY_DOMAIN_FILTER,
|
||||
cli_credentials_get_domain(cred));
|
||||
return cli_credentials_set_secrets(cred, SECRETS_PRIMARY_DOMAIN_DN,
|
||||
filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in credentials for the machine trust account, from the secrets database.
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
* @retval NTSTATUS error detailing any failure
|
||||
*/
|
||||
NTSTATUS cli_credentials_set_krbtgt(struct cli_credentials *cred)
|
||||
{
|
||||
char *filter;
|
||||
/* Bleh, nasty recursion issues: We are setting a machine
|
||||
* account here, so we don't want the 'pending' flag around
|
||||
* any more */
|
||||
cred->machine_account_pending = False;
|
||||
filter = talloc_asprintf(cred, SECRETS_KRBTGT_SEARCH,
|
||||
cli_credentials_get_realm(cred),
|
||||
cli_credentials_get_domain(cred));
|
||||
return cli_credentials_set_secrets(cred, SECRETS_PRINCIPALS_DN,
|
||||
filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in credentials for the machine trust account, from the secrets database.
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
* @retval NTSTATUS error detailing any failure
|
||||
*/
|
||||
NTSTATUS cli_credentials_set_stored_principal(struct cli_credentials *cred,
|
||||
const char *serviceprincipal)
|
||||
{
|
||||
char *filter;
|
||||
/* Bleh, nasty recursion issues: We are setting a machine
|
||||
* account here, so we don't want the 'pending' flag around
|
||||
* any more */
|
||||
cred->machine_account_pending = False;
|
||||
filter = talloc_asprintf(cred, SECRETS_PRINCIPAL_SEARCH,
|
||||
cli_credentials_get_realm(cred),
|
||||
cli_credentials_get_domain(cred),
|
||||
serviceprincipal);
|
||||
return cli_credentials_set_secrets(cred, SECRETS_PRINCIPALS_DN,
|
||||
filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask that when required, the credentials system will be filled with
|
||||
* machine trust account, from the secrets database.
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
* @note This function is used to call the above function after, rather
|
||||
* than during, popt processing.
|
||||
*
|
||||
*/
|
||||
void cli_credentials_set_machine_account_pending(struct cli_credentials *cred)
|
||||
{
|
||||
cred->machine_account_pending = True;
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS cli_credentials_update_all_keytabs(TALLOC_CTX *parent_ctx)
|
||||
{
|
||||
TALLOC_CTX *mem_ctx;
|
||||
int ldb_ret;
|
||||
struct ldb_context *ldb;
|
||||
struct ldb_message **msgs;
|
||||
const char *attrs[] = { NULL };
|
||||
struct cli_credentials *creds;
|
||||
const char *filter;
|
||||
NTSTATUS status;
|
||||
int i, ret;
|
||||
|
||||
mem_ctx = talloc_new(parent_ctx);
|
||||
if (!mem_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* Local secrets are stored in secrets.ldb */
|
||||
ldb = secrets_db_connect(mem_ctx);
|
||||
if (!ldb) {
|
||||
DEBUG(1, ("Could not open secrets.ldb\n"));
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/* search for the secret record, but only of things we can
|
||||
* actually update */
|
||||
ldb_ret = gendb_search(ldb,
|
||||
mem_ctx, NULL,
|
||||
&msgs, attrs,
|
||||
"(&(objectClass=kerberosSecret)(|(secret=*)(ntPwdHash=*)))");
|
||||
if (ldb_ret == -1) {
|
||||
DEBUG(1, ("Error looking for kerberos type secrets to push into a keytab:: %s", ldb_errstring(ldb)));
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
for (i=0; i < ldb_ret; i++) {
|
||||
/* Make a credentials structure from it */
|
||||
creds = cli_credentials_init(mem_ctx);
|
||||
if (!creds) {
|
||||
DEBUG(1, ("cli_credentials_init failed!"));
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
cli_credentials_set_conf(creds);
|
||||
filter = talloc_asprintf(mem_ctx, "dn=%s", ldb_dn_get_linearized(msgs[i]->dn));
|
||||
status = cli_credentials_set_secrets(creds, NULL, filter);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(1, ("Failed to read secrets for keytab update for %s\n",
|
||||
filter));
|
||||
continue;
|
||||
}
|
||||
ret = cli_credentials_update_keytab(creds);
|
||||
if (ret != 0) {
|
||||
DEBUG(1, ("Failed to update keytab for %s\n",
|
||||
filter));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,603 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Handle user credentials (as regards krb5)
|
||||
|
||||
Copyright (C) Jelmer Vernooij 2005
|
||||
Copyright (C) Tim Potter 2001
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "auth/kerberos/kerberos.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/credentials/credentials_krb5.h"
|
||||
|
||||
int cli_credentials_get_krb5_context(struct cli_credentials *cred,
|
||||
struct smb_krb5_context **smb_krb5_context)
|
||||
{
|
||||
int ret;
|
||||
if (cred->smb_krb5_context) {
|
||||
*smb_krb5_context = cred->smb_krb5_context;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = smb_krb5_init_context(cred, &cred->smb_krb5_context);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
*smb_krb5_context = cred->smb_krb5_context;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This needs to be called directly after the cli_credentials_init(),
|
||||
* otherwise we might have problems with the krb5 context already
|
||||
* being here.
|
||||
*/
|
||||
NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
|
||||
struct smb_krb5_context *smb_krb5_context)
|
||||
{
|
||||
if (!talloc_reference(cred, smb_krb5_context)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
cred->smb_krb5_context = smb_krb5_context;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
int cli_credentials_set_from_ccache(struct cli_credentials *cred,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
|
||||
krb5_principal princ;
|
||||
krb5_error_code ret;
|
||||
char *name;
|
||||
char **realm;
|
||||
|
||||
if (cred->ccache_obtained > obtained) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = krb5_cc_get_principal(cred->ccache->smb_krb5_context->krb5_context,
|
||||
cred->ccache->ccache, &princ);
|
||||
|
||||
if (ret) {
|
||||
char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred);
|
||||
DEBUG(1,("failed to get principal from ccache: %s\n",
|
||||
err_mess));
|
||||
talloc_free(err_mess);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = krb5_unparse_name(cred->ccache->smb_krb5_context->krb5_context, princ, &name);
|
||||
if (ret) {
|
||||
char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred);
|
||||
DEBUG(1,("failed to unparse principal from ccache: %s\n",
|
||||
err_mess));
|
||||
talloc_free(err_mess);
|
||||
return ret;
|
||||
}
|
||||
|
||||
realm = krb5_princ_realm(cred->ccache->smb_krb5_context->krb5_context, princ);
|
||||
|
||||
cli_credentials_set_principal(cred, name, obtained);
|
||||
|
||||
free(name);
|
||||
|
||||
krb5_free_principal(cred->ccache->smb_krb5_context->krb5_context, princ);
|
||||
|
||||
cred->ccache_obtained = obtained;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Free a memory ccache */
|
||||
static int free_mccache(struct ccache_container *ccc)
|
||||
{
|
||||
krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Free a disk-based ccache */
|
||||
static int free_dccache(struct ccache_container *ccc) {
|
||||
krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cli_credentials_set_ccache(struct cli_credentials *cred,
|
||||
const char *name,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_principal princ;
|
||||
struct ccache_container *ccc;
|
||||
if (cred->ccache_obtained > obtained) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ccc = talloc(cred, struct ccache_container);
|
||||
if (!ccc) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
|
||||
if (ret) {
|
||||
talloc_free(ccc);
|
||||
return ret;
|
||||
}
|
||||
talloc_reference(ccc, ccc->smb_krb5_context);
|
||||
|
||||
if (name) {
|
||||
ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
|
||||
if (ret) {
|
||||
DEBUG(1,("failed to read krb5 ccache: %s: %s\n",
|
||||
name,
|
||||
smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
|
||||
talloc_free(ccc);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
|
||||
if (ret) {
|
||||
DEBUG(3,("failed to read default krb5 ccache: %s\n",
|
||||
smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
|
||||
talloc_free(ccc);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_set_destructor(ccc, free_dccache);
|
||||
|
||||
ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
|
||||
|
||||
if (ret) {
|
||||
DEBUG(3,("failed to get principal from default ccache: %s\n",
|
||||
smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
|
||||
talloc_free(ccc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
|
||||
|
||||
cred->ccache = ccc;
|
||||
talloc_steal(cred, ccc);
|
||||
|
||||
ret = cli_credentials_set_from_ccache(cred, obtained);
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int cli_credentials_new_ccache(struct cli_credentials *cred, struct ccache_container **_ccc)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char *rand_string;
|
||||
struct ccache_container *ccc = talloc(cred, struct ccache_container);
|
||||
char *ccache_name;
|
||||
if (!ccc) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
rand_string = generate_random_str(NULL, 16);
|
||||
if (!rand_string) {
|
||||
talloc_free(ccc);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ccache_name = talloc_asprintf(ccc, "MEMORY:%s",
|
||||
rand_string);
|
||||
talloc_free(rand_string);
|
||||
|
||||
if (!ccache_name) {
|
||||
talloc_free(ccc);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
|
||||
if (ret) {
|
||||
talloc_free(ccc);
|
||||
return ret;
|
||||
}
|
||||
talloc_reference(ccc, ccc->smb_krb5_context);
|
||||
|
||||
ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, &ccc->ccache);
|
||||
if (ret) {
|
||||
DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n",
|
||||
ccache_name,
|
||||
smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
|
||||
talloc_free(ccache_name);
|
||||
talloc_free(ccc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
talloc_set_destructor(ccc, free_mccache);
|
||||
|
||||
cred->ccache = ccc;
|
||||
talloc_steal(cred, ccc);
|
||||
talloc_free(ccache_name);
|
||||
|
||||
if (_ccc) {
|
||||
*_ccc = ccc;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cli_credentials_get_ccache(struct cli_credentials *cred,
|
||||
struct ccache_container **ccc)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
|
||||
if (cred->ccache_obtained >= (MAX(cred->principal_obtained,
|
||||
cred->username_obtained))) {
|
||||
*ccc = cred->ccache;
|
||||
return 0;
|
||||
}
|
||||
if (cli_credentials_is_anonymous(cred)) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
ret = cli_credentials_new_ccache(cred, NULL);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
ret = kinit_to_ccache(cred, cred, cred->ccache->smb_krb5_context, cred->ccache->ccache);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
ret = cli_credentials_set_from_ccache(cred, cred->principal_obtained);
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
*ccc = cred->ccache;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int free_gssapi_creds(struct gssapi_creds_container *gcc)
|
||||
{
|
||||
OM_uint32 min_stat, maj_stat;
|
||||
maj_stat = gss_release_cred(&min_stat, &gcc->creds);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
|
||||
struct gssapi_creds_container **_gcc)
|
||||
{
|
||||
int ret = 0;
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
struct gssapi_creds_container *gcc;
|
||||
struct ccache_container *ccache;
|
||||
if (cred->client_gss_creds_obtained >= (MAX(cred->ccache_obtained,
|
||||
MAX(cred->principal_obtained,
|
||||
cred->username_obtained)))) {
|
||||
*_gcc = cred->client_gss_creds;
|
||||
return 0;
|
||||
}
|
||||
ret = cli_credentials_get_ccache(cred,
|
||||
&ccache);
|
||||
if (ret) {
|
||||
DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
gcc = talloc(cred, struct gssapi_creds_container);
|
||||
if (!gcc) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
|
||||
&gcc->creds);
|
||||
if (maj_stat) {
|
||||
if (min_stat) {
|
||||
ret = min_stat;
|
||||
} else {
|
||||
ret = EINVAL;
|
||||
}
|
||||
}
|
||||
if (ret == 0) {
|
||||
cred->client_gss_creds_obtained = cred->ccache_obtained;
|
||||
talloc_set_destructor(gcc, free_gssapi_creds);
|
||||
cred->client_gss_creds = gcc;
|
||||
*_gcc = gcc;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
Set a gssapi cred_id_t into the credentails system. (Client case)
|
||||
|
||||
This grabs the credentials both 'intact' and getting the krb5
|
||||
ccache out of it. This routine can be generalised in future for
|
||||
the case where we deal with GSSAPI mechs other than krb5.
|
||||
|
||||
On sucess, the caller must not free gssapi_cred, as it now belongs
|
||||
to the credentials system.
|
||||
*/
|
||||
|
||||
int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
|
||||
gss_cred_id_t gssapi_cred,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
int ret;
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
struct ccache_container *ccc;
|
||||
struct gssapi_creds_container *gcc;
|
||||
if (cred->client_gss_creds_obtained > obtained) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
gcc = talloc(cred, struct gssapi_creds_container);
|
||||
if (!gcc) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = cli_credentials_new_ccache(cred, &ccc);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
maj_stat = gss_krb5_copy_ccache(&min_stat,
|
||||
gssapi_cred, ccc->ccache);
|
||||
if (maj_stat) {
|
||||
if (min_stat) {
|
||||
ret = min_stat;
|
||||
} else {
|
||||
ret = EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
ret = cli_credentials_set_from_ccache(cred, obtained);
|
||||
}
|
||||
if (ret == 0) {
|
||||
gcc->creds = gssapi_cred;
|
||||
talloc_set_destructor(gcc, free_gssapi_creds);
|
||||
|
||||
cred->client_gss_creds_obtained = obtained;
|
||||
cred->client_gss_creds = gcc;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get the keytab (actually, a container containing the krb5_keytab)
|
||||
* attached to this context. If this hasn't been done or set before,
|
||||
* it will be generated from the password.
|
||||
*/
|
||||
int cli_credentials_get_keytab(struct cli_credentials *cred,
|
||||
struct keytab_container **_ktc)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
struct keytab_container *ktc;
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
|
||||
cred->username_obtained))) {
|
||||
*_ktc = cred->keytab;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cli_credentials_is_anonymous(cred)) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
mem_ctx = talloc_new(cred);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = smb_krb5_create_memory_keytab(mem_ctx, cred, smb_krb5_context, &ktc);
|
||||
if (ret) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
cred->keytab_obtained = (MAX(cred->principal_obtained,
|
||||
cred->username_obtained));
|
||||
|
||||
talloc_steal(cred, ktc);
|
||||
cred->keytab = ktc;
|
||||
*_ktc = cred->keytab;
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Given the name of a keytab (presumably in the format
|
||||
* FILE:/etc/krb5.keytab), open it and attach it */
|
||||
|
||||
int cli_credentials_set_keytab_name(struct cli_credentials *cred,
|
||||
const char *keytab_name,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
struct keytab_container *ktc;
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
if (cred->keytab_obtained >= obtained) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
mem_ctx = talloc_new(cred);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context,
|
||||
keytab_name, &ktc);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
cred->keytab_obtained = obtained;
|
||||
|
||||
talloc_steal(cred, ktc);
|
||||
cred->keytab = ktc;
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cli_credentials_update_keytab(struct cli_credentials *cred)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
struct keytab_container *ktc;
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
mem_ctx = talloc_new(cred);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
|
||||
if (ret) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_keytab(cred, &ktc);
|
||||
if (ret != 0) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = smb_krb5_update_keytab(mem_ctx, cred, smb_krb5_context, ktc);
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get server gss credentials (in gsskrb5, this means the keytab) */
|
||||
|
||||
int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
|
||||
struct gssapi_creds_container **_gcc)
|
||||
{
|
||||
int ret = 0;
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
struct gssapi_creds_container *gcc;
|
||||
struct keytab_container *ktc;
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
krb5_principal princ;
|
||||
|
||||
if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained,
|
||||
MAX(cred->principal_obtained,
|
||||
cred->username_obtained)))) {
|
||||
*_gcc = cred->server_gss_creds;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_keytab(cred,
|
||||
&ktc);
|
||||
if (ret) {
|
||||
DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
mem_ctx = talloc_new(cred);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ);
|
||||
if (ret) {
|
||||
DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gcc = talloc(cred, struct gssapi_creds_container);
|
||||
if (!gcc) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
/* This creates a GSSAPI cred_id_t with the principal and keytab set */
|
||||
maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
|
||||
&gcc->creds);
|
||||
if (maj_stat) {
|
||||
if (min_stat) {
|
||||
ret = min_stat;
|
||||
} else {
|
||||
ret = EINVAL;
|
||||
}
|
||||
}
|
||||
if (ret == 0) {
|
||||
cred->server_gss_creds_obtained = cred->keytab_obtained;
|
||||
talloc_set_destructor(gcc, free_gssapi_creds);
|
||||
cred->server_gss_creds = gcc;
|
||||
*_gcc = gcc;
|
||||
}
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Kerberos KVNO
|
||||
*/
|
||||
|
||||
void cli_credentials_set_kvno(struct cli_credentials *cred,
|
||||
int kvno)
|
||||
{
|
||||
cred->kvno = kvno;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Kerberos KVNO
|
||||
*/
|
||||
|
||||
int cli_credentials_get_kvno(struct cli_credentials *cred)
|
||||
{
|
||||
return cred->kvno;
|
||||
}
|
||||
|
||||
const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
|
||||
{
|
||||
return cred->salt_principal;
|
||||
}
|
||||
|
||||
void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
|
||||
{
|
||||
cred->salt_principal = talloc_strdup(cred, principal);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
samba -- Unix SMB/CIFS implementation.
|
||||
|
||||
Client credentials structure
|
||||
|
||||
Copyright (C) Jelmer Vernooij 2004-2006
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "heimdal/lib/gssapi/gssapi/gssapi.h"
|
||||
|
||||
struct ccache_container;
|
||||
|
||||
struct gssapi_creds_container {
|
||||
gss_cred_id_t creds;
|
||||
};
|
||||
|
||||
#include "auth/credentials/credentials_krb5_proto.h"
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
User credentials handling
|
||||
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
|
||||
#include "lib/crypto/crypto.h"
|
||||
#include "libcli/auth/libcli_auth.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
|
||||
void cli_credentials_get_ntlm_username_domain(struct cli_credentials *cred, TALLOC_CTX *mem_ctx,
|
||||
const char **username,
|
||||
const char **domain)
|
||||
{
|
||||
if (cred->principal_obtained > cred->username_obtained) {
|
||||
*domain = talloc_strdup(mem_ctx, "");
|
||||
*username = cli_credentials_get_principal(cred, mem_ctx);
|
||||
} else {
|
||||
*domain = cli_credentials_get_domain(cred);
|
||||
*username = cli_credentials_get_username(cred);
|
||||
}
|
||||
}
|
||||
|
||||
NTSTATUS cli_credentials_get_ntlm_response(struct cli_credentials *cred, TALLOC_CTX *mem_ctx,
|
||||
int *flags,
|
||||
DATA_BLOB challenge, DATA_BLOB target_info,
|
||||
DATA_BLOB *_lm_response, DATA_BLOB *_nt_response,
|
||||
DATA_BLOB *_lm_session_key, DATA_BLOB *_session_key)
|
||||
{
|
||||
const char *user, *domain;
|
||||
DATA_BLOB lm_response, nt_response;
|
||||
DATA_BLOB lm_session_key, session_key;
|
||||
const struct samr_Password *nt_hash;
|
||||
lm_session_key = data_blob(NULL, 0);
|
||||
|
||||
nt_hash = cli_credentials_get_nt_hash(cred, mem_ctx);
|
||||
|
||||
cli_credentials_get_ntlm_username_domain(cred, mem_ctx, &user, &domain);
|
||||
|
||||
/* If we are sending a username@realm login (see function
|
||||
* above), then we will not send LM, it will not be
|
||||
* accepted */
|
||||
if (cred->principal_obtained > cred->username_obtained) {
|
||||
*flags = *flags & ~CLI_CRED_LANMAN_AUTH;
|
||||
}
|
||||
|
||||
/* Likewise if we are a machine account (avoid protocol downgrade attacks) */
|
||||
if (cred->machine_account) {
|
||||
*flags = *flags & ~CLI_CRED_LANMAN_AUTH;
|
||||
}
|
||||
|
||||
if (cred->use_kerberos == CRED_MUST_USE_KERBEROS) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
if (!nt_hash) {
|
||||
static const uint8_t zeros[16];
|
||||
/* do nothing - blobs are zero length */
|
||||
|
||||
/* session key is all zeros */
|
||||
session_key = data_blob_talloc(mem_ctx, zeros, 16);
|
||||
lm_session_key = data_blob_talloc(mem_ctx, zeros, 16);
|
||||
|
||||
lm_response = data_blob(NULL, 0);
|
||||
nt_response = data_blob(NULL, 0);
|
||||
|
||||
/* not doing NTLM2 without a password */
|
||||
*flags &= ~CLI_CRED_NTLM2;
|
||||
} else if (*flags & CLI_CRED_NTLMv2_AUTH) {
|
||||
|
||||
if (!target_info.length) {
|
||||
/* be lazy, match win2k - we can't do NTLMv2 without it */
|
||||
DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* TODO: if the remote server is standalone, then we should replace 'domain'
|
||||
with the server name as supplied above */
|
||||
|
||||
if (!SMBNTLMv2encrypt_hash(mem_ctx,
|
||||
user,
|
||||
domain,
|
||||
nt_hash->hash, &challenge,
|
||||
&target_info,
|
||||
&lm_response, &nt_response,
|
||||
NULL, &session_key)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* LM Key is incompatible... */
|
||||
*flags &= ~CLI_CRED_LANMAN_AUTH;
|
||||
} else if (*flags & CLI_CRED_NTLM2) {
|
||||
struct MD5Context md5_session_nonce_ctx;
|
||||
uint8_t session_nonce[16];
|
||||
uint8_t session_nonce_hash[16];
|
||||
uint8_t user_session_key[16];
|
||||
|
||||
lm_response = data_blob_talloc(mem_ctx, NULL, 24);
|
||||
generate_random_buffer(lm_response.data, 8);
|
||||
memset(lm_response.data+8, 0, 16);
|
||||
|
||||
memcpy(session_nonce, challenge.data, 8);
|
||||
memcpy(&session_nonce[8], lm_response.data, 8);
|
||||
|
||||
MD5Init(&md5_session_nonce_ctx);
|
||||
MD5Update(&md5_session_nonce_ctx, challenge.data, 8);
|
||||
MD5Update(&md5_session_nonce_ctx, lm_response.data, 8);
|
||||
MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
|
||||
|
||||
DEBUG(5, ("NTLMSSP challenge set by NTLM2\n"));
|
||||
DEBUG(5, ("challenge is: \n"));
|
||||
dump_data(5, session_nonce_hash, 8);
|
||||
|
||||
nt_response = data_blob_talloc(mem_ctx, NULL, 24);
|
||||
SMBOWFencrypt(nt_hash->hash,
|
||||
session_nonce_hash,
|
||||
nt_response.data);
|
||||
|
||||
session_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
|
||||
SMBsesskeygen_ntv1(nt_hash->hash, user_session_key);
|
||||
hmac_md5(user_session_key, session_nonce, sizeof(session_nonce), session_key.data);
|
||||
dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
|
||||
|
||||
/* LM Key is incompatible... */
|
||||
*flags &= ~CLI_CRED_LANMAN_AUTH;
|
||||
} else {
|
||||
uint8_t lm_hash[16];
|
||||
nt_response = data_blob_talloc(mem_ctx, NULL, 24);
|
||||
SMBOWFencrypt(nt_hash->hash, challenge.data,
|
||||
nt_response.data);
|
||||
|
||||
session_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
SMBsesskeygen_ntv1(nt_hash->hash, session_key.data);
|
||||
dump_data_pw("NT session key:\n", session_key.data, session_key.length);
|
||||
|
||||
/* lanman auth is insecure, it may be disabled.
|
||||
We may also not have a password */
|
||||
if (*flags & CLI_CRED_LANMAN_AUTH) {
|
||||
const char *password;
|
||||
password = cli_credentials_get_password(cred);
|
||||
if (!password) {
|
||||
lm_response = nt_response;
|
||||
} else {
|
||||
lm_response = data_blob_talloc(mem_ctx, NULL, 24);
|
||||
if (!SMBencrypt(password,challenge.data,
|
||||
lm_response.data)) {
|
||||
/* If the LM password was too long (and therefore the LM hash being
|
||||
of the first 14 chars only), don't send it.
|
||||
|
||||
We don't have any better options but to send the NT response
|
||||
*/
|
||||
data_blob_free(&lm_response);
|
||||
lm_response = nt_response;
|
||||
/* LM Key is incompatible with 'long' passwords */
|
||||
*flags &= ~CLI_CRED_LANMAN_AUTH;
|
||||
} else {
|
||||
E_deshash(password, lm_hash);
|
||||
lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
memcpy(lm_session_key.data, lm_hash, 8);
|
||||
memset(&lm_session_key.data[8], '\0', 8);
|
||||
|
||||
if (!(*flags & CLI_CRED_NTLM_AUTH)) {
|
||||
session_key = lm_session_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const char *password;
|
||||
|
||||
/* LM Key is incompatible... */
|
||||
lm_response = nt_response;
|
||||
*flags &= ~CLI_CRED_LANMAN_AUTH;
|
||||
|
||||
password = cli_credentials_get_password(cred);
|
||||
if (password) {
|
||||
E_deshash(password, lm_hash);
|
||||
lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
memcpy(lm_session_key.data, lm_hash, 8);
|
||||
memset(&lm_session_key.data[8], '\0', 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_lm_response) {
|
||||
*_lm_response = lm_response;
|
||||
}
|
||||
if (_nt_response) {
|
||||
*_nt_response = nt_response;
|
||||
}
|
||||
if (_lm_session_key) {
|
||||
*_lm_session_key = lm_session_key;
|
||||
}
|
||||
if (_session_key) {
|
||||
*_session_key = session_key;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user