diff options
-rw-r--r-- | source3/Makefile.in | 3 | ||||
-rw-r--r-- | source3/configure.in | 12 | ||||
-rw-r--r-- | source3/lib/system.c | 25 | ||||
-rw-r--r-- | source3/nsswitch/winbindd.c | 3 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_ccache_access.c | 282 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_dual.c | 1 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_nss.h | 24 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_pam.c | 4 | ||||
-rw-r--r-- | source3/utils/ntlm_auth.c | 109 |
9 files changed, 448 insertions, 15 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in index 8f4f50a41a..c6ea72c92f 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -729,7 +729,8 @@ WINBINDD_OBJ1 = \ nsswitch/winbindd_dual.o \ nsswitch/winbindd_async.o \ nsswitch/winbindd_creds.o \ - nsswitch/winbindd_cred_cache.o + nsswitch/winbindd_cred_cache.o \ + nsswitch/winbindd_ccache_access.o WINBINDD_OBJ = \ $(WINBINDD_OBJ1) $(PASSDB_OBJ) $(GROUPDB_OBJ) \ diff --git a/source3/configure.in b/source3/configure.in index 850b3cd9d3..dc2f516139 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -5395,6 +5395,18 @@ AC_CHECK_MEMBERS([struct secmethod_table.method_attrlist], , , AC_CHECK_MEMBERS([struct secmethod_table.method_version], , , [#include <usersec.h>]) +AC_CACHE_CHECK([for SO_PEERCRED],samba_cv_HAVE_PEERCRED,[ +AC_TRY_COMPILE([#include <sys/types.h> +#include <sys/socket.h>], +[struct ucred cred; + socklen_t cred_len; + int ret = getsockopt(0, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len); +], +samba_cv_HAVE_PEERCRED=yes,samba_cv_HAVE_PEERCRED=no,samba_cv_HAVE_PEERCRED=cross)]) +if test x"$samba_cv_HAVE_PEERCRED" = x"yes"; then + AC_DEFINE(HAVE_PEERCRED,1,[Whether we can use SO_PEERCRED to get socket credentials]) +fi + ################################################# # Check to see if we should use the included popt diff --git a/source3/lib/system.c b/source3/lib/system.c index 24c726b8f7..bd7e4b8a67 100644 --- a/source3/lib/system.c +++ b/source3/lib/system.c @@ -2242,3 +2242,28 @@ int sys_aio_suspend(const SMB_STRUCT_AIOCB * const cblist[], int n, const struct return -1; } #endif /* WITH_AIO */ + +int getpeereid( int s, uid_t *uid) +{ +#if defined(HAVE_PEERCRED) + struct ucred cred; + socklen_t cred_len = sizeof(struct ucred); + int ret; + + ret = getsockopt(s, SOL_SOCKET, SO_PEERCRED, (void *)&cred, &cred_len); + if (ret != 0) { + return -1; + } + + if (cred_len != sizeof(struct ucred)) { + errno = EINVAL; + return -1; + } + + *uid = cred.uid; + return 0; +#else + errno = ENOSYS; + return -1; +#endif +} diff --git a/source3/nsswitch/winbindd.c b/source3/nsswitch/winbindd.c index a4cd724e00..f6e742ac20 100644 --- a/source3/nsswitch/winbindd.c +++ b/source3/nsswitch/winbindd.c @@ -253,6 +253,9 @@ static struct winbindd_dispatch_table { "WINBINDD_PRIV_PIPE_DIR" }, { WINBINDD_GETDCNAME, winbindd_getdcname, "GETDCNAME" }, + /* Credential cache access */ + { WINBINDD_CCACHE_NTLMAUTH, winbindd_ccache_ntlm_auth, "NTLMAUTH" }, + /* WINS functions */ { WINBINDD_WINS_BYNAME, winbindd_wins_byname, "WINS_BYNAME" }, diff --git a/source3/nsswitch/winbindd_ccache_access.c b/source3/nsswitch/winbindd_ccache_access.c new file mode 100644 index 0000000000..35d2cb4e51 --- /dev/null +++ b/source3/nsswitch/winbindd_ccache_access.c @@ -0,0 +1,282 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon - cached credentials funcions + + Copyright (C) Robert O'Callahan 2006 + Copyright (C) Jeremy Allison 2006 (minor fixes to fit into Samba and + protect against integer wrap). + + 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 "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +static BOOL client_can_access_ccache_entry(uid_t client_uid, + struct WINBINDD_CCACHE_ENTRY *entry) +{ + if (client_uid == entry->uid || client_uid == 0) { + DEBUG(10, ("Access granted to uid %d\n", client_uid)); + return True; + } + + DEBUG(1, ("Access denied to uid %d (expected %d)\n", client_uid, entry->uid)); + return False; +} + +static NTSTATUS do_ntlm_auth_with_password(const char *username, + const char *domain, + const char *password, + const DATA_BLOB initial_msg, + const DATA_BLOB challenge_msg, + DATA_BLOB *auth_msg) +{ + NTSTATUS status; + NTLMSSP_STATE *ntlmssp_state = NULL; + DATA_BLOB dummy_msg, reply; + + status = ntlmssp_client_start(&ntlmssp_state); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not start NTLMSSP client: %s\n", + nt_errstr(status))); + goto done; + } + + status = ntlmssp_set_username(ntlmssp_state, username); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not set username: %s\n", + nt_errstr(status))); + goto done; + } + + status = ntlmssp_set_domain(ntlmssp_state, domain); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not set domain: %s\n", + nt_errstr(status))); + goto done; + } + + status = ntlmssp_set_password(ntlmssp_state, password); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not set password: %s\n", + nt_errstr(status))); + goto done; + } + + /* We need to get our protocol handler into the right state. So first + we ask it to generate the initial message. Actually the client has already + sent its own initial message, so we're going to drop this one on the floor. + The client might have sent a different message, for example with different + negotiation options, but as far as I can tell this won't hurt us. (Unless + the client sent a different username or domain, in which case that's their + problem for telling us the wrong username or domain.) + Since we have a copy of the initial message that the client sent, we could + resolve any discrepancies if we had to. + */ + dummy_msg = data_blob(NULL, 0); + reply = data_blob(NULL, 0); + status = ntlmssp_update(ntlmssp_state, dummy_msg, &reply); + data_blob_free(&dummy_msg); + data_blob_free(&reply); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + DEBUG(1, ("Failed to create initial message! [%s]\n", + nt_errstr(status))); + goto done; + } + + /* Now we are ready to handle the server's actual response. */ + status = ntlmssp_update(ntlmssp_state, challenge_msg, &reply); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + DEBUG(1, ("We didn't get a response to the challenge! [%s]\n", + nt_errstr(status))); + data_blob_free(&reply); + goto done; + } + *auth_msg = reply; + status = NT_STATUS_OK; + +done: + ntlmssp_end(&ntlmssp_state); + return status; +} + +static BOOL check_client_uid(struct winbindd_cli_state *state, uid_t uid) +{ + int ret; + uid_t ret_uid; + + ret_uid = (uid_t)-1; + + ret = getpeereid(state->sock, &ret_uid); + if (ret != 0) { + DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; " + "denying access\n", strerror(errno))); + return False; + } + + if (uid != ret_uid) { + DEBUG(1, ("check_client_uid: Client lied about its uid: said %d, " + "actually was %d; denying access\n", + uid, ret_uid)); + return False; + } + + return True; +} + +void winbindd_ccache_ntlm_auth(struct winbindd_cli_state *state) +{ + struct winbindd_domain *domain; + fstring name_domain, name_user; + + /* Ensure null termination */ + state->request.data.ccache_ntlm_auth.user[ + sizeof(state->request.data.ccache_ntlm_auth.user)-1]='\0'; + + DEBUG(3, ("[%5lu]: perform NTLM auth on behalf of user %s\n", (unsigned long)state->pid, + state->request.data.ccache_ntlm_auth.user)); + + /* Parse domain and username */ + + if (!parse_domain_user(state->request.data.ccache_ntlm_auth.user, + name_domain, name_user)) { + DEBUG(5,("winbindd_ccache_ntlm_auth: cannot parse domain and user from name [%s]\n", + state->request.data.ccache_ntlm_auth.user)); + request_error(state); + return; + } + + domain = find_auth_domain(state, name_domain); + + if (domain == NULL) { + DEBUG(5,("winbindd_ccache_ntlm_auth: can't get domain [%s]\n", + name_domain)); + request_error(state); + return; + } + + if (!check_client_uid(state, state->request.data.ccache_ntlm_auth.uid)) { + request_error(state); + return; + } + + sendto_domain(state, domain); +} + +enum winbindd_result winbindd_dual_ccache_ntlm_auth(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + NTSTATUS result = NT_STATUS_NOT_SUPPORTED; + struct WINBINDD_CCACHE_ENTRY *entry; + DATA_BLOB initial, challenge, auth; + fstring name_domain, name_user; + uint32 initial_blob_len, challenge_blob_len, extra_len; + + /* Ensure null termination */ + state->request.data.ccache_ntlm_auth.user[ + sizeof(state->request.data.ccache_ntlm_auth.user)-1]='\0'; + + DEBUG(3, ("winbindd_dual_ccache_ntlm_auth: [%5lu]: perform NTLM auth on " + "behalf of user %s (dual)\n", (unsigned long)state->pid, + state->request.data.ccache_ntlm_auth.user)); + + /* validate blob lengths */ + initial_blob_len = state->request.data.ccache_ntlm_auth.initial_blob_len; + challenge_blob_len = state->request.data.ccache_ntlm_auth.challenge_blob_len; + extra_len = state->request.extra_len; + + if (initial_blob_len > extra_len || challenge_blob_len > extra_len || + initial_blob_len + challenge_blob_len > extra_len || + initial_blob_len + challenge_blob_len < initial_blob_len || + initial_blob_len + challenge_blob_len < challenge_blob_len) { + + DEBUG(10,("winbindd_dual_ccache_ntlm_auth: blob lengths overrun " + "or wrap. Buffer [%d+%d > %d]\n", + initial_blob_len, + challenge_blob_len, + extra_len)); + goto process_result; + } + + /* Parse domain and username */ + if (!parse_domain_user(state->request.data.ccache_ntlm_auth.user, name_domain, name_user)) { + DEBUG(10,("winbindd_dual_ccache_ntlm_auth: cannot parse " + "domain and user from name [%s]\n", + state->request.data.ccache_ntlm_auth.user)); + goto process_result; + } + + entry = get_ccache_by_username(state->request.data.ccache_ntlm_auth.user); + if (entry == NULL) { + DEBUG(10,("winbindd_dual_ccache_ntlm_auth: could not find " + "credentials for user %s\n", + state->request.data.ccache_ntlm_auth.user)); + goto process_result; + } + + DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry->ccname)); + + if (!client_can_access_ccache_entry(state->request.data.ccache_ntlm_auth.uid, entry)) { + goto process_result; + } + + if (initial_blob_len == 0 && challenge_blob_len == 0) { + /* this is just a probe to see if credentials are available. */ + result = NT_STATUS_OK; + state->response.data.ccache_ntlm_auth.auth_blob_len = 0; + goto process_result; + } + + initial = data_blob(state->request.extra_data.data, initial_blob_len); + challenge = data_blob(state->request.extra_data.data + initial_blob_len, + state->request.data.ccache_ntlm_auth.challenge_blob_len); + + if (!initial.data || !challenge.data) { + result = NT_STATUS_NO_MEMORY; + } else { + result = do_ntlm_auth_with_password(name_user, name_domain, entry->pass, + initial, challenge, &auth); + } + + data_blob_free(&initial); + data_blob_free(&challenge); + + if (!NT_STATUS_IS_OK(result)) { + goto process_result; + } + + state->response.extra_data.data = smb_xmemdup(auth.data, auth.length); + if (!state->response.extra_data.data) { + result = NT_STATUS_NO_MEMORY; + goto process_result; + } + state->response.length += auth.length; + state->response.data.ccache_ntlm_auth.auth_blob_len = auth.length; + + data_blob_free(&auth); + + process_result: + return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; +} diff --git a/source3/nsswitch/winbindd_dual.c b/source3/nsswitch/winbindd_dual.c index 32d85688b6..55f897603c 100644 --- a/source3/nsswitch/winbindd_dual.c +++ b/source3/nsswitch/winbindd_dual.c @@ -369,6 +369,7 @@ static struct winbindd_child_dispatch_table child_dispatch_table[] = { { WINBINDD_ALLOCATE_GID, winbindd_dual_allocate_gid, "ALLOCATE_GID" }, { WINBINDD_GETUSERDOMGROUPS, winbindd_dual_getuserdomgroups, "GETUSERDOMGROUPS" }, { WINBINDD_DUAL_GETSIDALIASES, winbindd_dual_getsidaliases, "GETSIDALIASES" }, + { WINBINDD_CCACHE_NTLMAUTH, winbindd_dual_ccache_ntlm_auth, "CCACHE_NTLM_AUTH" }, /* End of list */ { WINBINDD_NUM_CMDS, NULL, "NONE" } diff --git a/source3/nsswitch/winbindd_nss.h b/source3/nsswitch/winbindd_nss.h index 4a95a3cf42..b19026b4db 100644 --- a/source3/nsswitch/winbindd_nss.h +++ b/source3/nsswitch/winbindd_nss.h @@ -35,7 +35,7 @@ /* Update this when you change the interface. */ -#define WINBIND_INTERFACE_VERSION 16 +#define WINBIND_INTERFACE_VERSION 17 /* Have to deal with time_t being 4 or 8 bytes due to structure alignment. On a 64bit Linux box, we have to support a constant structure size @@ -153,6 +153,10 @@ enum winbindd_cmd { WINBINDD_DUAL_USERINFO, WINBINDD_DUAL_GETSIDALIASES, + /* Complete the challenge phase of the NTLM authentication + protocol using cached password. */ + WINBINDD_CCACHE_NTLMAUTH, + WINBINDD_NUM_CMDS }; @@ -292,8 +296,21 @@ struct winbindd_request { } dual_idmapset; BOOL list_all_domains; + struct { + uid_t uid; + fstring user; + /* the effective uid of the client, must be the uid for 'user'. + This is checked by the main daemon, trusted by children. */ + /* if the blobs are length zero, then this doesn't + produce an actual challenge response. It merely + succeeds if there are cached credentials available + that could be used. */ + uint32 initial_blob_len; /* blobs in extra_data */ + uint32 challenge_blob_len; + } ccache_ntlm_auth; + /* padding -- needed to fix alignment between 32bit and 64bit libs. - The size if the sizeof the union without the padding aligned on + The size is the sizeof the union without the padding aligned on an 8 byte boundary. --jerry */ char padding[1560]; @@ -426,6 +443,9 @@ struct winbindd_response { fstring shell; uint32 group_rid; } user_info; + struct { + uint32 auth_blob_len; /* blob in extra_data */ + } ccache_ntlm_auth; } data; /* Variable length return data */ diff --git a/source3/nsswitch/winbindd_pam.c b/source3/nsswitch/winbindd_pam.c index 66d297090b..365b277160 100644 --- a/source3/nsswitch/winbindd_pam.c +++ b/source3/nsswitch/winbindd_pam.c @@ -200,8 +200,8 @@ static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx, return NT_STATUS_LOGON_FAILURE; } -static struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state, - const char *domain_name) +struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state, + const char *domain_name) { struct winbindd_domain *domain; diff --git a/source3/utils/ntlm_auth.c b/source3/utils/ntlm_auth.c index f53b226fbb..1bed7794da 100644 --- a/source3/utils/ntlm_auth.c +++ b/source3/utils/ntlm_auth.c @@ -6,6 +6,7 @@ Copyright (C) Tim Potter 2000-2003 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004 Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000 + Copyright (C) Robert O'Callahan 2006 (added cached credential code). 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 @@ -92,6 +93,7 @@ static DATA_BLOB opt_lm_response; static DATA_BLOB opt_nt_response; static int request_lm_key; static int request_user_session_key; +static int use_cached_creds; static const char *require_membership_of; static const char *require_membership_of_sid; @@ -583,14 +585,17 @@ static NTSTATUS ntlm_auth_start_ntlmssp_client(NTLMSSP_STATE **client_ntlmssp_st return status; } - status = ntlmssp_set_password(*client_ntlmssp_state, opt_password); + if (opt_password) { + status = ntlmssp_set_password(*client_ntlmssp_state, opt_password); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Could not set password: %s\n", - nt_errstr(status))); - ntlmssp_end(client_ntlmssp_state); - return status; + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not set password: %s\n", + nt_errstr(status))); + ntlmssp_end(client_ntlmssp_state); + return status; + } } + return NT_STATUS_OK; } @@ -617,6 +622,63 @@ static NTSTATUS ntlm_auth_start_ntlmssp_server(NTLMSSP_STATE **ntlmssp_state) return NT_STATUS_OK; } +/******************************************************************* + Used by firefox to drive NTLM auth to IIS servers. Currently + requires krb5 enabled in winbindd as only then are the credentials + cached in memory. This needs fixing in winbindd. JRA. +*******************************************************************/ + +static NTSTATUS do_ccache_ntlm_auth(DATA_BLOB initial_msg, DATA_BLOB challenge_msg, + DATA_BLOB *reply) +{ + struct winbindd_request wb_request; + struct winbindd_response wb_response; + NSS_STATUS result; + + /* get winbindd to do the ntlmssp step on our behalf */ + ZERO_STRUCT(wb_request); + ZERO_STRUCT(wb_response); + + fstr_sprintf(wb_request.data.ccache_ntlm_auth.user, + "%s%c%s", opt_domain, winbind_separator(), opt_username); + wb_request.data.ccache_ntlm_auth.uid = geteuid(); + wb_request.data.ccache_ntlm_auth.initial_blob_len = initial_msg.length; + wb_request.data.ccache_ntlm_auth.challenge_blob_len = challenge_msg.length; + wb_request.extra_len = initial_msg.length + challenge_msg.length; + + if (wb_request.extra_len > 0) { + wb_request.extra_data.data = SMB_MALLOC_ARRAY(char, wb_request.extra_len); + if (wb_request.extra_data.data == NULL) { + return NT_STATUS_NO_MEMORY; + } + + memcpy(wb_request.extra_data.data, initial_msg.data, initial_msg.length); + memcpy(wb_request.extra_data.data + initial_msg.length, + challenge_msg.data, challenge_msg.length); + } + + result = winbindd_request_response(WINBINDD_CCACHE_NTLMAUTH, &wb_request, &wb_response); + SAFE_FREE(wb_request.extra_data.data); + + if (result != NSS_STATUS_SUCCESS) { + free_response(&wb_response); + return NT_STATUS_UNSUCCESSFUL; + } + + if (reply) { + *reply = data_blob(wb_response.extra_data.data, + wb_response.data.ccache_ntlm_auth.auth_blob_len); + if (wb_response.data.ccache_ntlm_auth.auth_blob_len > 0 && + reply->data == NULL) { + free_response(&wb_response); + return NT_STATUS_NO_MEMORY; + } + } + + free_response(&wb_response); + return NT_STATUS_MORE_PROCESSING_REQUIRED; +} + static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode, char *buf, int length) { @@ -737,7 +799,12 @@ static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mod static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode, char *buf, int length) { + /* The statics here are *HORRIBLE* and this entire concept + needs to be rewritten. Essentially it's using these statics + as the state in a state machine. BLEEEGH ! JRA. */ + static NTLMSSP_STATE *ntlmssp_state = NULL; + static DATA_BLOB initial_message; static char* want_feature_list = NULL; static uint32 neg_flags = 0; static BOOL have_session_key = False; @@ -782,7 +849,18 @@ static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mo return; } - if (opt_password == NULL) { + if (!ntlmssp_state && use_cached_creds) { + /* check whether credentials are usable. */ + DATA_BLOB empty_blob = data_blob(NULL, 0); + + nt_status = do_ccache_ntlm_auth(empty_blob, empty_blob, NULL); + if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + /* failed to use cached creds */ + use_cached_creds = False; + } + } + + if (opt_password == NULL && !use_cached_creds) { /* Request a password from the calling process. After sending it, the calling process should retry asking for the negotiate. */ @@ -829,12 +907,17 @@ static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mo } ntlmssp_want_feature_list(ntlmssp_state, want_feature_list); first = True; + initial_message = data_blob(NULL, 0); } DEBUG(10, ("got NTLMSSP packet:\n")); dump_data(10, (const char *)request.data, request.length); - nt_status = ntlmssp_update(ntlmssp_state, request, &reply); + if (use_cached_creds && !opt_password && !first) { + nt_status = do_ccache_ntlm_auth(initial_message, request, &reply); + } else { + nt_status = ntlmssp_update(ntlmssp_state, request, &reply); + } if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { char *reply_base64 = base64_encode_data_blob(reply); @@ -844,7 +927,11 @@ static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mo x_fprintf(x_stdout, "KK %s\n", reply_base64); } SAFE_FREE(reply_base64); - data_blob_free(&reply); + if (first) { + initial_message = reply; + } else { + data_blob_free(&reply); + } DEBUG(10, ("NTLMSSP challenge\n")); } else if (NT_STATUS_IS_OK(nt_status)) { char *reply_base64 = base64_encode_data_blob(reply); @@ -2081,7 +2168,8 @@ enum { OPT_LM_KEY, OPT_USER_SESSION_KEY, OPT_DIAGNOSTICS, - OPT_REQUIRE_MEMBERSHIP + OPT_REQUIRE_MEMBERSHIP, + OPT_USE_CACHED_CREDS }; int main(int argc, const char **argv) @@ -2116,6 +2204,7 @@ enum { { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"}, { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"}, { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"}, + { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"}, { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"}, { "require-membership-of", 0, POPT_ARG_STRING, &require_membership_of, OPT_REQUIRE_MEMBERSHIP, "Require that a user be a member of this group (either name or SID) for authentication to succeed" }, POPT_COMMON_SAMBA |