diff options
Diffstat (limited to 'source3/nsswitch')
-rw-r--r-- | source3/nsswitch/libwbclient/wbc_guid.c | 118 | ||||
-rw-r--r-- | source3/nsswitch/libwbclient/wbc_idmap.c | 2 | ||||
-rw-r--r-- | source3/nsswitch/libwbclient/wbc_pam.c | 301 | ||||
-rw-r--r-- | source3/nsswitch/libwbclient/wbc_pwd.c | 2 | ||||
-rw-r--r-- | source3/nsswitch/libwbclient/wbc_sid.c | 6 | ||||
-rw-r--r-- | source3/nsswitch/libwbclient/wbc_util.c | 186 | ||||
-rw-r--r-- | source3/nsswitch/libwbclient/wbclient.h | 114 | ||||
-rw-r--r-- | source3/nsswitch/pam_winbind.c | 1132 | ||||
-rw-r--r-- | source3/nsswitch/pam_winbind.h | 74 | ||||
-rw-r--r-- | source3/nsswitch/wbinfo.c | 14 | ||||
-rw-r--r-- | source3/nsswitch/winbind_krb5_locator.c | 52 | ||||
-rw-r--r-- | source3/nsswitch/winbind_struct_protocol.h | 17 |
12 files changed, 1521 insertions, 497 deletions
diff --git a/source3/nsswitch/libwbclient/wbc_guid.c b/source3/nsswitch/libwbclient/wbc_guid.c new file mode 100644 index 0000000000..0cb33e9868 --- /dev/null +++ b/source3/nsswitch/libwbclient/wbc_guid.c @@ -0,0 +1,118 @@ +/* + Unix SMB/CIFS implementation. + + Winbind client API + + Copyright (C) Gerald (Jerry) Carter 2007 + + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Required Headers */ + +#include "libwbclient.h" + +/** @brief Convert a binary GUID to a character string + * + * @param guid Binary Guid + * @param **guid_string Resulting character string + * + * @return #wbcErr + **/ + +wbcErr wbcGuidToString(const struct wbcGuid *guid, + char **guid_string) +{ + wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE; + + if (!guid) { + wbc_status = WBC_ERR_INVALID_PARAM; + BAIL_ON_WBC_ERROR(wbc_status); + } + + *guid_string = talloc_asprintf(NULL, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + guid->time_low, guid->time_mid, + guid->time_hi_and_version, + guid->clock_seq[0], + guid->clock_seq[1], + guid->node[0], guid->node[1], + guid->node[2], guid->node[3], + guid->node[4], guid->node[5]); + BAIL_ON_PTR_ERROR((*guid_string), wbc_status); + + wbc_status = WBC_ERR_SUCCESS; + +done: + return wbc_status; +} + +/** @brief Convert a character string to a binary GUID + * + * @param *str Character string + * @param guid Resulting binary GUID + * + * @return #wbcErr + **/ + +wbcErr wbcStringToGuid(const char *str, + struct wbcGuid *guid) +{ + uint32_t time_low; + uint32_t time_mid, time_hi_and_version; + uint32_t clock_seq[2]; + uint32_t node[6]; + int i; + wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE; + + if (!guid) { + wbc_status = WBC_ERR_INVALID_PARAM; + BAIL_ON_WBC_ERROR(wbc_status); + } + + if (!str) { + wbc_status = WBC_ERR_INVALID_PARAM; + BAIL_ON_WBC_ERROR(wbc_status); + } + + if (11 == sscanf(str, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + &time_low, &time_mid, &time_hi_and_version, + &clock_seq[0], &clock_seq[1], + &node[0], &node[1], &node[2], &node[3], &node[4], &node[5])) { + wbc_status = WBC_ERR_SUCCESS; + } else if (11 == sscanf(str, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", + &time_low, &time_mid, &time_hi_and_version, + &clock_seq[0], &clock_seq[1], + &node[0], &node[1], &node[2], &node[3], &node[4], &node[5])) { + wbc_status = WBC_ERR_SUCCESS; + } + + BAIL_ON_WBC_ERROR(wbc_status); + + guid->time_low = time_low; + guid->time_mid = time_mid; + guid->time_hi_and_version = time_hi_and_version; + guid->clock_seq[0] = clock_seq[0]; + guid->clock_seq[1] = clock_seq[1]; + + for (i=0;i<6;i++) { + guid->node[i] = node[i]; + } + + wbc_status = WBC_ERR_SUCCESS; + +done: + return wbc_status; +} diff --git a/source3/nsswitch/libwbclient/wbc_idmap.c b/source3/nsswitch/libwbclient/wbc_idmap.c index e32d66cd71..1615fd33ee 100644 --- a/source3/nsswitch/libwbclient/wbc_idmap.c +++ b/source3/nsswitch/libwbclient/wbc_idmap.c @@ -394,7 +394,7 @@ wbcErr wbcSetUidHwm(uid_t uid_hwm) /** @brief Set the highwater mark for allocated gids. * - * @param uid_hwm The new gid highwater mark value + * @param gid_hwm The new gid highwater mark value * * @return #wbcErr **/ diff --git a/source3/nsswitch/libwbclient/wbc_pam.c b/source3/nsswitch/libwbclient/wbc_pam.c index 20b42b6efb..713ba2e65b 100644 --- a/source3/nsswitch/libwbclient/wbc_pam.c +++ b/source3/nsswitch/libwbclient/wbc_pam.c @@ -4,6 +4,7 @@ Winbind client API Copyright (C) Gerald (Jerry) Carter 2007 + Copyright (C) Guenther Deschner 2008 This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -260,6 +261,50 @@ done: return wbc_status; } +static wbcErr wbc_create_logon_info(TALLOC_CTX *mem_ctx, + const struct winbindd_response *resp, + struct wbcLogonUserInfo **_i) +{ + wbcErr wbc_status = WBC_ERR_SUCCESS; + struct wbcLogonUserInfo *i; + + i = talloc_zero(mem_ctx, struct wbcLogonUserInfo); + BAIL_ON_PTR_ERROR(i, wbc_status); + + wbc_status = wbc_create_auth_info(i, resp, &i->info); + BAIL_ON_WBC_ERROR(wbc_status); + + if (resp->data.auth.krb5ccname) { + wbc_status = wbcAddNamedBlob(&i->num_blobs, + &i->blobs, + "krb5ccname", + 0, + (uint8_t *)resp->data.auth.krb5ccname, + strlen(resp->data.auth.krb5ccname)+1); + BAIL_ON_WBC_ERROR(wbc_status); + } + + if (resp->data.auth.unix_username) { + wbc_status = wbcAddNamedBlob(&i->num_blobs, + &i->blobs, + "unix_username", + 0, + (uint8_t *)resp->data.auth.unix_username, + strlen(resp->data.auth.unix_username)+1); + BAIL_ON_WBC_ERROR(wbc_status); + } + + *_i = i; + i = NULL; +done: + if (!WBC_ERROR_IS_OK(wbc_status) && i) { + wbcFreeMemory(i->blobs); + } + + talloc_free(i); + return wbc_status; +} + /** @brief Authenticate with more detailed information * * @param params Input parameters, WBC_AUTH_USER_LEVEL_HASH @@ -331,6 +376,7 @@ wbcErr wbcAuthenticateUserEx(const struct wbcAuthUserParams *params, params->account_name, sizeof(request.data.auth.user)-1); } + strncpy(request.data.auth.pass, params->password.plaintext, sizeof(request.data.auth.pass)-1); @@ -416,6 +462,10 @@ wbcErr wbcAuthenticateUserEx(const struct wbcAuthUserParams *params, BAIL_ON_WBC_ERROR(wbc_status); } + if (params->flags) { + request.flags |= params->flags; + } + wbc_status = wbcRequestResponse(cmd, &request, &response); @@ -497,6 +547,101 @@ wbcErr wbcCheckTrustCredentials(const char *domain, return wbc_status; } +/** @brief Trigger an extended logoff notification to Winbind for a specific user + * + * @param params A wbcLogoffUserParams structure + * @param error User output details on error + * + * @return #wbcErr + * + **/ + +wbcErr wbcLogoffUserEx(const struct wbcLogoffUserParams *params, + struct wbcAuthErrorInfo **error) +{ + struct winbindd_request request; + struct winbindd_response response; + wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE; + int i; + + /* validate input */ + + if (!params || !params->username) { + wbc_status = WBC_ERR_INVALID_PARAM; + BAIL_ON_WBC_ERROR(wbc_status); + } + + if ((params->num_blobs > 0) && (params->blobs == NULL)) { + wbc_status = WBC_ERR_INVALID_PARAM; + BAIL_ON_WBC_ERROR(wbc_status); + } + if ((params->num_blobs == 0) && (params->blobs != NULL)) { + wbc_status = WBC_ERR_INVALID_PARAM; + BAIL_ON_WBC_ERROR(wbc_status); + } + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + strncpy(request.data.logoff.user, params->username, + sizeof(request.data.logoff.user)-1); + + for (i=0; i<params->num_blobs; i++) { + + if (strcasecmp(params->blobs[i].name, "ccfilename") == 0) { + if (params->blobs[i].blob.data) { + strncpy(request.data.logoff.krb5ccname, + (const char *)params->blobs[i].blob.data, + sizeof(request.data.logoff.krb5ccname) - 1); + } + continue; + } + + if (strcasecmp(params->blobs[i].name, "user_uid") == 0) { + if (params->blobs[i].blob.data) { + memcpy(&request.data.logoff.uid, + params->blobs[i].blob.data, + MIN(params->blobs[i].blob.length, + sizeof(request.data.logoff.uid))); + } + continue; + } + + if (strcasecmp(params->blobs[i].name, "flags") == 0) { + if (params->blobs[i].blob.data) { + memcpy(&request.flags, + params->blobs[i].blob.data, + MIN(params->blobs[i].blob.length, + sizeof(request.flags))); + } + continue; + } + } + + /* Send request */ + + wbc_status = wbcRequestResponse(WINBINDD_PAM_LOGOFF, + &request, + &response); + + /* Take the response above and return it to the caller */ + if (response.data.auth.nt_status != 0) { + if (error) { + wbc_status = wbc_create_error_info(NULL, + &response, + error); + BAIL_ON_WBC_ERROR(wbc_status); + } + + wbc_status = WBC_ERR_AUTH_ERROR; + BAIL_ON_WBC_ERROR(wbc_status); + } + BAIL_ON_WBC_ERROR(wbc_status); + + done: + return wbc_status; +} + /** @brief Trigger a logoff notification to Winbind for a specific user * * @param username Name of user to remove from Winbind's list of @@ -794,3 +939,159 @@ wbcErr wbcChangeUserPassword(const char *username, done: return wbc_status; } + +/** @brief Logon a User + * + * @param[in] params Pointer to a wbcLogonUserParams structure + * @param[out] info Pointer to a pointer to a wbcLogonUserInfo structure + * @param[out] error Pointer to a pointer to a wbcAuthErrorInfo structure + * @param[out] policy Pointer to a pointer to a wbcUserPasswordPolicyInfo structure + * + * @return #wbcErr + * + **/ + +wbcErr wbcLogonUser(const struct wbcLogonUserParams *params, + struct wbcLogonUserInfo **info, + struct wbcAuthErrorInfo **error, + struct wbcUserPasswordPolicyInfo **policy) +{ + wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE; + int cmd = 0; + struct winbindd_request request; + struct winbindd_response response; + uint32_t i; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + if (info) { + *info = NULL; + } + if (error) { + *error = NULL; + } + if (policy) { + *policy = NULL; + } + + if (!params) { + wbc_status = WBC_ERR_INVALID_PARAM; + BAIL_ON_WBC_ERROR(wbc_status); + } + + if (!params->username) { + wbc_status = WBC_ERR_INVALID_PARAM; + BAIL_ON_WBC_ERROR(wbc_status); + } + + if ((params->num_blobs > 0) && (params->blobs == NULL)) { + wbc_status = WBC_ERR_INVALID_PARAM; + BAIL_ON_WBC_ERROR(wbc_status); + } + if ((params->num_blobs == 0) && (params->blobs != NULL)) { + wbc_status = WBC_ERR_INVALID_PARAM; + BAIL_ON_WBC_ERROR(wbc_status); + } + + /* Initialize request */ + + cmd = WINBINDD_PAM_AUTH; + request.flags = WBFLAG_PAM_INFO3_TEXT | + WBFLAG_PAM_USER_SESSION_KEY | + WBFLAG_PAM_LMKEY; + + if (!params->password) { + wbc_status = WBC_ERR_INVALID_PARAM; + BAIL_ON_WBC_ERROR(wbc_status); + } + + strncpy(request.data.auth.user, + params->username, + sizeof(request.data.auth.user)-1); + + strncpy(request.data.auth.pass, + params->password, + sizeof(request.data.auth.pass)-1); + + for (i=0; i<params->num_blobs; i++) { + + if (strcasecmp(params->blobs[i].name, "krb5_cc_type") == 0) { + if (params->blobs[i].blob.data) { + strncpy(request.data.auth.krb5_cc_type, + (const char *)params->blobs[i].blob.data, + sizeof(request.data.auth.krb5_cc_type) - 1); + } + continue; + } + + if (strcasecmp(params->blobs[i].name, "user_uid") == 0) { + if (params->blobs[i].blob.data) { + memcpy(&request.data.auth.uid, + params->blobs[i].blob.data, + MIN(sizeof(request.data.auth.uid), + params->blobs[i].blob.length)); + } + continue; + } + + if (strcasecmp(params->blobs[i].name, "flags") == 0) { + if (params->blobs[i].blob.data) { + uint32_t flags; + memcpy(&flags, + params->blobs[i].blob.data, + MIN(sizeof(flags), + params->blobs[i].blob.length)); + request.flags |= flags; + } + continue; + } + + if (strcasecmp(params->blobs[i].name, "membership_of") == 0) { + if (params->blobs[i].blob.data && + params->blobs[i].blob.data[0] > 0) { + strncpy(request.data.auth.require_membership_of_sid, + (const char *)params->blobs[i].blob.data, + sizeof(request.data.auth.require_membership_of_sid) - 1); + } + continue; + } + } + + wbc_status = wbcRequestResponse(cmd, + &request, + &response); + + if (response.data.auth.nt_status != 0) { + if (error) { + wbc_status = wbc_create_error_info(NULL, + &response, + error); + BAIL_ON_WBC_ERROR(wbc_status); + } + + wbc_status = WBC_ERR_AUTH_ERROR; + BAIL_ON_WBC_ERROR(wbc_status); + } + BAIL_ON_WBC_ERROR(wbc_status); + + if (info) { + wbc_status = wbc_create_logon_info(NULL, + &response, + info); + BAIL_ON_WBC_ERROR(wbc_status); + } + + if (policy) { + wbc_status = wbc_create_password_policy_info(NULL, + &response, + policy); + BAIL_ON_WBC_ERROR(wbc_status); + } + +done: + if (response.extra_data.data) + free(response.extra_data.data); + + return wbc_status; +} diff --git a/source3/nsswitch/libwbclient/wbc_pwd.c b/source3/nsswitch/libwbclient/wbc_pwd.c index b5f167369c..0d17b312ef 100644 --- a/source3/nsswitch/libwbclient/wbc_pwd.c +++ b/source3/nsswitch/libwbclient/wbc_pwd.c @@ -380,7 +380,7 @@ wbcErr wbcGetgrent(struct group **grp) * * @param *account The given user name * @param *num_groups Number of elements returned in the groups array - * @param **groups Pointer to resulting gid_t array. + * @param **_groups Pointer to resulting gid_t array. * * @return #wbcErr **/ diff --git a/source3/nsswitch/libwbclient/wbc_sid.c b/source3/nsswitch/libwbclient/wbc_sid.c index f4ffa4e5ca..4cfdd792b5 100644 --- a/source3/nsswitch/libwbclient/wbc_sid.c +++ b/source3/nsswitch/libwbclient/wbc_sid.c @@ -223,9 +223,9 @@ wbcErr wbcLookupName(const char *domain, /** @brief Convert a SID to a domain and name * * @param *sid Pointer to the domain SID to be resolved - * @param domain Resolved Domain name (possibly "") - * @param name Resolved User or group name - * @param *name_type Pointet to the resolved SID type + * @param pdomain Resolved Domain name (possibly "") + * @param pname Resolved User or group name + * @param *pname_type Pointet to the resolved SID type * * @return #wbcErr * diff --git a/source3/nsswitch/libwbclient/wbc_util.c b/source3/nsswitch/libwbclient/wbc_util.c index 24568f9101..b4868748ae 100644 --- a/source3/nsswitch/libwbclient/wbc_util.c +++ b/source3/nsswitch/libwbclient/wbc_util.c @@ -496,7 +496,7 @@ wbcErr wbcListTrusts(struct wbcDomainInfo **domains, size_t *num_domains) /** @brief Enumerate the domain trusts known by Winbind * * @param domain Name of the domain to query for a DC - * @flags Bit flags used to control the domain location query + * @param flags Bit flags used to control the domain location query * @param *dc_info Pointer to the returned domain controller information * * @return #wbcErr @@ -550,3 +550,187 @@ done: return wbc_status; } + +static wbcErr wbc_create_domain_controller_info_ex(TALLOC_CTX *mem_ctx, + const struct winbindd_response *resp, + struct wbcDomainControllerInfoEx **_i) +{ + wbcErr wbc_status = WBC_ERR_SUCCESS; + struct wbcDomainControllerInfoEx *i; + struct wbcGuid guid; + + i = talloc(mem_ctx, struct wbcDomainControllerInfoEx); + BAIL_ON_PTR_ERROR(i, wbc_status); + + i->dc_unc = talloc_strdup(i, resp->data.dsgetdcname.dc_unc); + BAIL_ON_PTR_ERROR(i->dc_unc, wbc_status); + + i->dc_address = talloc_strdup(i, resp->data.dsgetdcname.dc_address); + BAIL_ON_PTR_ERROR(i->dc_address, wbc_status); + + i->dc_address_type = resp->data.dsgetdcname.dc_address_type; + + wbc_status = wbcStringToGuid(resp->data.dsgetdcname.domain_guid, &guid); + if (WBC_ERROR_IS_OK(wbc_status)) { + i->domain_guid = talloc(i, struct wbcGuid); + BAIL_ON_PTR_ERROR(i->domain_guid, wbc_status); + + *i->domain_guid = guid; + } else { + i->domain_guid = NULL; + } + + i->domain_name = talloc_strdup(i, resp->data.dsgetdcname.domain_name); + BAIL_ON_PTR_ERROR(i->domain_name, wbc_status); + + if (resp->data.dsgetdcname.forest_name[0] != '\0') { + i->forest_name = talloc_strdup(i, + resp->data.dsgetdcname.forest_name); + BAIL_ON_PTR_ERROR(i->forest_name, wbc_status); + } else { + i->forest_name = NULL; + } + + i->dc_flags = resp->data.dsgetdcname.dc_flags; + + if (resp->data.dsgetdcname.dc_site_name[0] != '\0') { + i->dc_site_name = talloc_strdup(i, + resp->data.dsgetdcname.dc_site_name); + BAIL_ON_PTR_ERROR(i->dc_site_name, wbc_status); + } else { + i->dc_site_name = NULL; + } + + if (resp->data.dsgetdcname.client_site_name[0] != '\0') { + i->client_site_name = talloc_strdup(i, + resp->data.dsgetdcname.client_site_name); + BAIL_ON_PTR_ERROR(i->client_site_name, wbc_status); + } else { + i->client_site_name = NULL; + } + + *_i = i; + i = NULL; + +done: + talloc_free(i); + return wbc_status; +} + +/** @brief Get extended domain controller information + * + * @param domain Name of the domain to query for a DC + * @param guid Guid of the domain to query for a DC + * @param site Site of the domain to query for a DC + * @param flags Bit flags used to control the domain location query + * @param *dc_info Pointer to the returned extended domain controller information + * + * @return #wbcErr + * + **/ + +wbcErr wbcLookupDomainControllerEx(const char *domain, + struct wbcGuid *guid, + const char *site, + uint32_t flags, + struct wbcDomainControllerInfoEx **dc_info) +{ + wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE; + struct winbindd_request request; + struct winbindd_response response; + + /* validate input params */ + + if (!domain || !dc_info) { + wbc_status = WBC_ERR_INVALID_PARAM; + BAIL_ON_WBC_ERROR(wbc_status); + } + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + request.data.dsgetdcname.flags = flags; + + strncpy(request.data.dsgetdcname.domain_name, domain, + sizeof(request.data.dsgetdcname.domain_name)-1); + + if (site) { + strncpy(request.data.dsgetdcname.site_name, site, + sizeof(request.data.dsgetdcname.site_name)-1); + } + + if (guid) { + char *str = NULL; + + wbc_status = wbcGuidToString(guid, &str); + BAIL_ON_WBC_ERROR(wbc_status); + + strncpy(request.data.dsgetdcname.domain_guid, str, + sizeof(request.data.dsgetdcname.domain_guid)-1); + + wbcFreeMemory(str); + } + + /* Send request */ + + wbc_status = wbcRequestResponse(WINBINDD_DSGETDCNAME, + &request, + &response); + BAIL_ON_WBC_ERROR(wbc_status); + + if (dc_info) { + wbc_status = wbc_create_domain_controller_info_ex(NULL, + &response, + dc_info); + BAIL_ON_WBC_ERROR(wbc_status); + } + + wbc_status = WBC_ERR_SUCCESS; +done: + return wbc_status; +} + +/** @brief Initialize a named blob and add to list of blobs + * + * @param[in,out] num_blobs Pointer to the number of blobs + * @param[in,out] blobs Pointer to an array of blobs + * @param[in] name Name of the new named blob + * @param[in] flags Flags of the new named blob + * @param[in] data Blob data of new blob + * @param[in] length Blob data length of new blob + * + * @return #wbcErr + * + **/ + +wbcErr wbcAddNamedBlob(size_t *num_blobs, + struct wbcNamedBlob **blobs, + const char *name, + uint32_t flags, + uint8_t *data, + size_t length) +{ + wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE; + struct wbcNamedBlob blob; + + *blobs = talloc_realloc(NULL, *blobs, struct wbcNamedBlob, + *(num_blobs)+1); + BAIL_ON_PTR_ERROR(*blobs, wbc_status); + + blob.name = talloc_strdup(*blobs, name); + BAIL_ON_PTR_ERROR(blob.name, wbc_status); + blob.flags = flags; + blob.blob.length = length; + blob.blob.data = (uint8_t *)talloc_memdup(*blobs, data, length); + BAIL_ON_PTR_ERROR(blob.blob.data, wbc_status); + + (*(blobs))[*num_blobs] = blob; + *(num_blobs) += 1; + + wbc_status = WBC_ERR_SUCCESS; +done: + if (!WBC_ERROR_IS_OK(wbc_status) && blobs) { + wbcFreeMemory(*blobs); + } + return wbc_status; +} diff --git a/source3/nsswitch/libwbclient/wbclient.h b/source3/nsswitch/libwbclient/wbclient.h index cae3feec5b..00a3c98966 100644 --- a/source3/nsswitch/libwbclient/wbclient.h +++ b/source3/nsswitch/libwbclient/wbclient.h @@ -137,6 +137,19 @@ struct wbcSidWithAttr { #define WBC_SID_ATTR_GROUP_LOGON_ID 0xC0000000 /** + * @brief Windows GUID + * + **/ + +struct wbcGuid { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq[2]; + uint8_t node[6]; +}; + +/** * @brief Domain Information **/ @@ -206,6 +219,36 @@ struct wbcAuthUserParams { }; /** + * @brief Generic Blob + **/ + +struct wbcBlob { + uint8_t *data; + size_t length; +}; + +/** + * @brief Named Blob + **/ + +struct wbcNamedBlob { + const char *name; + uint32_t flags; + struct wbcBlob blob; +}; + +/** + * @brief Logon User Parameters + **/ + +struct wbcLogonUserParams { + const char *username; + const char *password; + size_t num_blobs; + struct wbcNamedBlob *blobs; +}; + +/** * @brief ChangePassword Parameters **/ @@ -297,6 +340,18 @@ struct wbcAuthUserInfo { struct wbcSidWithAttr *sids; }; +/** + * @brief Logon User Information + * + * Some of the strings are maybe NULL + **/ + +struct wbcLogonUserInfo { + struct wbcAuthUserInfo *info; + size_t num_blobs; + struct wbcNamedBlob *blobs; +}; + /* wbcAuthUserInfo->user_flags */ #define WBC_AUTH_USER_INFO_GUEST 0x00000001 @@ -372,6 +427,16 @@ enum wbcPasswordChangeRejectReason { WBC_PWD_CHANGE_REJECT_COMPLEXITY=5 }; +/** + * @brief Logoff User Parameters + **/ + +struct wbcLogoffUserParams { + const char *username; + size_t num_blobs; + struct wbcNamedBlob *blobs; +}; + /* * DomainControllerInfo struct */ @@ -379,7 +444,20 @@ struct wbcDomainControllerInfo { char *dc_name; }; - +/* + * DomainControllerInfoEx struct + */ +struct wbcDomainControllerInfoEx { + const char *dc_unc; + const char *dc_address; + uint16_t dc_address_type; + struct wbcGuid *domain_guid; + const char *domain_name; + const char *forest_name; + uint32_t dc_flags; + const char *dc_site_name; + const char *client_site_name; +}; /* * Memory Management @@ -398,6 +476,16 @@ wbcErr wbcSidToString(const struct wbcDomainSid *sid, wbcErr wbcStringToSid(const char *sid_string, struct wbcDomainSid *sid); +/* + * Utility functions for dealing with GUIDs + */ + +wbcErr wbcGuidToString(const struct wbcGuid *guid, + char **guid_string); + +wbcErr wbcStringToGuid(const char *guid_string, + struct wbcGuid *guid); + wbcErr wbcPing(void); wbcErr wbcLibraryDetails(struct wbcLibraryDetails **details); @@ -531,6 +619,12 @@ wbcErr wbcLookupDomainController(const char *domain, uint32_t flags, struct wbcDomainControllerInfo **dc_info); +wbcErr wbcLookupDomainControllerEx(const char *domain, + struct wbcGuid *guid, + const char *site, + uint32_t flags, + struct wbcDomainControllerInfoEx **dc_info); + /* * Athenticate functions */ @@ -542,10 +636,18 @@ wbcErr wbcAuthenticateUserEx(const struct wbcAuthUserParams *params, struct wbcAuthUserInfo **info, struct wbcAuthErrorInfo **error); +wbcErr wbcLogonUser(const struct wbcLogonUserParams *params, + struct wbcLogonUserInfo **info, + struct wbcAuthErrorInfo **error, + struct wbcUserPasswordPolicyInfo **policy); + wbcErr wbcLogoffUser(const char *username, uid_t uid, const char *ccfilename); +wbcErr wbcLogoffUserEx(const struct wbcLogoffUserParams *params, + struct wbcAuthErrorInfo **error); + wbcErr wbcChangeUserPassword(const char *username, const char *old_password, const char *new_password); @@ -566,6 +668,14 @@ wbcErr wbcResolveWinsByIP(const char *ip, char **name); */ wbcErr wbcCheckTrustCredentials(const char *domain, struct wbcAuthErrorInfo **error); - +/* + * Helper functions + */ +wbcErr wbcAddNamedBlob(size_t *num_blobs, + struct wbcNamedBlob **blobs, + const char *name, + uint32_t flags, + uint8_t *data, + size_t length); #endif /* _WBCLIENT_H */ diff --git a/source3/nsswitch/pam_winbind.c b/source3/nsswitch/pam_winbind.c index beb0d47baa..8d8868d0ef 100644 --- a/source3/nsswitch/pam_winbind.c +++ b/source3/nsswitch/pam_winbind.c @@ -12,6 +12,42 @@ #include "pam_winbind.h" +static int wbc_error_to_pam_error(wbcErr status) +{ + switch (status) { + case WBC_ERR_SUCCESS: + return PAM_SUCCESS; + case WBC_ERR_NOT_IMPLEMENTED: + return PAM_SERVICE_ERR; + case WBC_ERR_UNKNOWN_FAILURE: + break; + case WBC_ERR_NO_MEMORY: + return PAM_BUF_ERR; + case WBC_ERR_INVALID_SID: + case WBC_ERR_INVALID_PARAM: + break; + case WBC_ERR_WINBIND_NOT_AVAILABLE: + return PAM_AUTHINFO_UNAVAIL; + case WBC_ERR_DOMAIN_NOT_FOUND: + return PAM_AUTHINFO_UNAVAIL; + case WBC_ERR_INVALID_RESPONSE: + return PAM_BUF_ERR; + case WBC_ERR_NSS_ERROR: + return PAM_USER_UNKNOWN; + case WBC_ERR_AUTH_ERROR: + return PAM_AUTH_ERR; + case WBC_ERR_UNKNOWN_USER: + return PAM_USER_UNKNOWN; + case WBC_ERR_UNKNOWN_GROUP: + return PAM_USER_UNKNOWN; + case WBC_ERR_PWD_CHANGE_FAILED: + break; + } + + /* be paranoid */ + return PAM_AUTH_ERR; +} + static const char *_pam_error_code_str(int err) { switch (err) { @@ -401,6 +437,10 @@ static int _pam_parse(const pam_handle_t *pamh, ctrl |= WINBIND_WARN_PWD_EXPIRE; } + if (iniparser_getboolean(d, "global:mkhomedir", false)) { + ctrl |= WINBIND_MKHOMEDIR; + } + config_from_pam: /* step through arguments */ for (i=argc,v=argv; i-- > 0; ++v) { @@ -433,6 +473,8 @@ config_from_pam: ctrl |= WINBIND_KRB5_CCACHE_TYPE; else if (!strcasecmp(*v, "cached_login")) ctrl |= WINBIND_CACHED_LOGIN; + else if (!strcasecmp(*v, "mkhomedir")) + ctrl |= WINBIND_MKHOMEDIR; else { __pam_log(pamh, ctrl, LOG_ERR, "pam_parse: unknown option: %s", *v); @@ -452,13 +494,17 @@ config_from_pam: return ctrl; }; -static void _pam_winbind_free_context(struct pwb_context *ctx) +static int _pam_winbind_free_context(struct pwb_context *ctx) { + if (!ctx) { + return 0; + } + if (ctx->dict) { iniparser_freedict(ctx->dict); } - SAFE_FREE(ctx); + return 0; } static int _pam_winbind_init_context(pam_handle_t *pamh, @@ -469,12 +515,12 @@ static int _pam_winbind_init_context(pam_handle_t *pamh, { struct pwb_context *r = NULL; - r = (struct pwb_context *)malloc(sizeof(struct pwb_context)); + r = TALLOC_ZERO_P(NULL, struct pwb_context); if (!r) { return PAM_BUF_ERR; } - ZERO_STRUCTP(r); + talloc_set_destructor(r, _pam_winbind_free_context); r->pamh = pamh; r->flags = flags; @@ -482,7 +528,7 @@ static int _pam_winbind_init_context(pam_handle_t *pamh, r->argv = argv; r->ctrl = _pam_parse(pamh, flags, argc, argv, &r->dict); if (r->ctrl == -1) { - _pam_winbind_free_context(r); + TALLOC_FREE(r); return PAM_SYSTEM_ERR; } @@ -502,7 +548,7 @@ static void _pam_winbind_cleanup_func(pam_handle_t *pamh, "(error_status = %d)", pamh, data, error_status); } - SAFE_FREE(data); + TALLOC_FREE(data); } @@ -644,80 +690,11 @@ static int _make_remark_format(struct pwb_context *ctx, int type, const char *fo return ret; } -static int pam_winbind_request(struct pwb_context *ctx, - enum winbindd_cmd req_type, - struct winbindd_request *request, - struct winbindd_response *response) -{ - /* Fill in request and send down pipe */ - winbindd_init_request(request, req_type); - - if (winbind_write_sock(request, sizeof(*request), 0, 0) == -1) { - _pam_log(ctx, LOG_ERR, - "pam_winbind_request: write to socket failed!"); - winbind_close_sock(); - return PAM_SERVICE_ERR; - } - - /* Wait for reply */ - if (winbindd_read_reply(response) == -1) { - _pam_log(ctx, LOG_ERR, - "pam_winbind_request: read from socket failed!"); - winbind_close_sock(); - return PAM_SERVICE_ERR; - } - - /* We are done with the socket - close it and avoid mischeif */ - winbind_close_sock(); - - /* Copy reply data from socket */ - if (response->result == WINBINDD_OK) { - return PAM_SUCCESS; - } - - /* no need to check for pam_error codes for getpwnam() */ - switch (req_type) { - - case WINBINDD_GETPWNAM: - case WINBINDD_LOOKUPNAME: - if (strlen(response->data.auth.nt_status_string) > 0) { - _pam_log(ctx, LOG_ERR, - "request failed, NT error was %s", - response->data.auth.nt_status_string); - } else { - _pam_log(ctx, LOG_ERR, "request failed"); - } - return PAM_USER_UNKNOWN; - default: - break; - } - - if (response->data.auth.pam_error != PAM_SUCCESS) { - _pam_log(ctx, LOG_ERR, - "request failed: %s, " - "PAM error was %s (%d), NT error was %s", - response->data.auth.error_string, - pam_strerror(ctx->pamh, response->data.auth.pam_error), - response->data.auth.pam_error, - response->data.auth.nt_status_string); - return response->data.auth.pam_error; - } - - _pam_log(ctx, LOG_ERR, "request failed, but PAM error 0!"); - - return PAM_SERVICE_ERR; -} - static int pam_winbind_request_log(struct pwb_context *ctx, - enum winbindd_cmd req_type, - struct winbindd_request *request, - struct winbindd_response *response, - const char *user) + int retval, + const char *user, + const char *fn) { - int retval; - - retval = pam_winbind_request(ctx, req_type, request, response); - switch (retval) { case PAM_AUTH_ERR: /* incorrect password */ @@ -749,33 +726,65 @@ static int pam_winbind_request_log(struct pwb_context *ctx, return retval; case PAM_SUCCESS: /* Otherwise, the authentication looked good */ - switch (req_type) { - case WINBINDD_INFO: - break; - case WINBINDD_PAM_AUTH: - _pam_log(ctx, LOG_NOTICE, - "user '%s' granted access", user); - break; - case WINBINDD_PAM_CHAUTHTOK: - _pam_log(ctx, LOG_NOTICE, - "user '%s' password changed", user); - break; - default: - _pam_log(ctx, LOG_NOTICE, - "user '%s' OK", user); - break; + if (strcmp(fn, "wbcLogonUser") == 0) { + _pam_log(ctx, LOG_NOTICE, + "user '%s' granted access", user); + } else { + _pam_log(ctx, LOG_NOTICE, + "user '%s' OK", user); } - return retval; default: /* we don't know anything about this return value */ _pam_log(ctx, LOG_ERR, - "internal module error (retval = %d, user = '%s')", - retval, user); + "internal module error (retval = %s(%d), user = '%s')", + _pam_error_code_str(retval), retval, user); return retval; } } +static int wbc_auth_error_to_pam_error(struct pwb_context *ctx, + struct wbcAuthErrorInfo *e, + wbcErr status, + const char *username, + const char *fn) +{ + int ret = PAM_AUTH_ERR; + + if (WBC_ERROR_IS_OK(status)) { + _pam_log_debug(ctx, LOG_DEBUG, "request %s succeeded", + fn); + ret = PAM_SUCCESS; + return pam_winbind_request_log(ctx, ret, username, fn); + } + + if (e) { + if (e->pam_error != PAM_SUCCESS) { + _pam_log(ctx, LOG_ERR, + "request %s failed: %s, " + "PAM error: %s (%d), NTSTATUS: %s, " + "Error message was: %s", + fn, + wbcErrorString(status), + _pam_error_code_str(e->pam_error), + e->pam_error, + e->nt_string, + e->display_string); + ret = e->pam_error; + return pam_winbind_request_log(ctx, ret, username, fn); + } + + _pam_log(ctx, LOG_ERR, "request %s failed, but PAM error 0!", fn); + + ret = PAM_SERVICE_ERR; + return pam_winbind_request_log(ctx, ret, username, fn); + } + + ret = wbc_error_to_pam_error(status); + return pam_winbind_request_log(ctx, ret, username, fn); +} + + /** * send a password expiry message if required * @@ -848,29 +857,34 @@ static bool _pam_send_password_expiry_message(struct pwb_context *ctx, */ static void _pam_warn_password_expiry(struct pwb_context *ctx, - const struct winbindd_response *response, + const struct wbcAuthUserInfo *info, + const struct wbcUserPasswordPolicyInfo *policy, int warn_pwd_expire, bool *already_expired) { time_t now = time(NULL); time_t next_change = 0; + if (!info || !policy) { + return; + } + if (already_expired) { *already_expired = false; } - /* accounts with ACB_PWNOEXP set never receive a warning */ - if (response->data.auth.info3.acct_flags & ACB_PWNOEXP) { + /* accounts with WBC_ACB_PWNOEXP set never receive a warning */ + if (info->acct_flags & WBC_ACB_PWNOEXP) { return; } /* no point in sending a warning if this is a grace logon */ - if (PAM_WB_GRACE_LOGON(response->data.auth.info3.user_flgs)) { + if (PAM_WB_GRACE_LOGON(info->user_flags)) { return; } /* check if the info3 must change timestamp has been set */ - next_change = response->data.auth.info3.pass_must_change_time; + next_change = info->pass_must_change_time; if (_pam_send_password_expiry_message(ctx, next_change, now, warn_pwd_expire, @@ -881,12 +895,11 @@ static void _pam_warn_password_expiry(struct pwb_context *ctx, /* now check for the global password policy */ /* good catch from Ralf Haferkamp: an expiry of "never" is translated * to -1 */ - if (response->data.auth.policy.expire <= 0) { + if (policy->expire <= 0) { return; } - next_change = response->data.auth.info3.pass_last_set_time + - response->data.auth.policy.expire; + next_change = info->pass_last_set_time + policy->expire; if (_pam_send_password_expiry_message(ctx, next_change, now, warn_pwd_expire, @@ -944,33 +957,33 @@ static bool winbind_name_to_sid_string(struct pwb_context *ctx, int sid_list_buffer_size) { const char* sid_string; - struct winbindd_response sid_response; /* lookup name? */ if (IS_SID_STRING(name)) { sid_string = name; } else { - struct winbindd_request sid_request; - - ZERO_STRUCT(sid_request); - ZERO_STRUCT(sid_response); + wbcErr wbc_status; + struct wbcDomainSid sid; + enum wbcSidType type; + char *sid_str; _pam_log_debug(ctx, LOG_DEBUG, "no sid given, looking up: %s\n", name); - /* fortunatly winbindd can handle non-separated names */ - strncpy(sid_request.data.name.name, name, - sizeof(sid_request.data.name.name) - 1); - - if (pam_winbind_request_log(ctx, WINBINDD_LOOKUPNAME, - &sid_request, &sid_response, - user)) { + wbc_status = wbcLookupName("", name, &sid, &type); + if (!WBC_ERROR_IS_OK(wbc_status)) { _pam_log(ctx, LOG_INFO, "could not lookup name: %s\n", name); return false; } - sid_string = sid_response.data.sid.sid; + wbc_status = wbcSidToString(&sid, &sid_str); + if (!WBC_ERROR_IS_OK(wbc_status)) { + return false; + } + + wbcFreeMemory(sid_str); + sid_string = sid_str; } if (!safe_append_string(sid_list_buffer, sid_string, @@ -1055,15 +1068,28 @@ out: */ static void _pam_setup_krb5_env(struct pwb_context *ctx, - const char *krb5ccname) + struct wbcLogonUserInfo *info) { char var[PATH_MAX]; int ret; + uint32_t i; + const char *krb5ccname = NULL; if (off(ctx->ctrl, WINBIND_KRB5_AUTH)) { return; } + if (!info) { + return; + } + + for (i=0; i < info->num_blobs; i++) { + if (strcasecmp(info->blobs[i].name, "krb5ccname") == 0) { + krb5ccname = (const char *)info->blobs[i].blob.data; + break; + } + } + if (!krb5ccname || (strlen(krb5ccname) == 0)) { return; } @@ -1084,6 +1110,41 @@ static void _pam_setup_krb5_env(struct pwb_context *ctx, } /** + * Copy unix username if available (further processed in PAM). + * + * @param ctx PAM winbind context + * @param user_ret A pointer that holds a pointer to a string + * @param unix_username A username + * + * @return void. + */ + +static void _pam_setup_unix_username(struct pwb_context *ctx, + char **user_ret, + struct wbcLogonUserInfo *info) +{ + const char *unix_username = NULL; + uint32_t i; + + if (!user_ret || !info) { + return; + } + + for (i=0; i < info->num_blobs; i++) { + if (strcasecmp(info->blobs[i].name, "unix_username") == 0) { + unix_username = (const char *)info->blobs[i].blob.data; + break; + } + } + + if (!unix_username || !unix_username[0]) { + return; + } + + *user_ret = strdup(unix_username); +} + +/** * Set string into the PAM stack. * * @param ctx PAM winbind context. @@ -1104,14 +1165,13 @@ static void _pam_set_data_string(struct pwb_context *ctx, return; } - ret = pam_set_data(ctx->pamh, data_name, (void *)strdup(value), + ret = pam_set_data(ctx->pamh, data_name, talloc_strdup(NULL, value), _pam_winbind_cleanup_func); if (ret) { _pam_log_debug(ctx, LOG_DEBUG, "Could not set data %s: %s\n", data_name, pam_strerror(ctx->pamh, ret)); } - } /** @@ -1125,16 +1185,16 @@ static void _pam_set_data_string(struct pwb_context *ctx, */ static void _pam_set_data_info3(struct pwb_context *ctx, - struct winbindd_response *response) + const struct wbcAuthUserInfo *info) { _pam_set_data_string(ctx, PAM_WINBIND_HOMEDIR, - response->data.auth.info3.home_dir); + info->home_directory); _pam_set_data_string(ctx, PAM_WINBIND_LOGONSCRIPT, - response->data.auth.info3.logon_script); + info->logon_script); _pam_set_data_string(ctx, PAM_WINBIND_LOGONSERVER, - response->data.auth.info3.logon_srv); + info->logon_server); _pam_set_data_string(ctx, PAM_WINBIND_PROFILEPATH, - response->data.auth.info3.profile_path); + info->profile_path); } /** @@ -1216,80 +1276,235 @@ static void _pam_warn_krb5_failure(struct pwb_context *ctx, } } +static bool _pam_check_remark_auth_err(struct pwb_context *ctx, + const struct wbcAuthErrorInfo *e, + const char *nt_status_string, + int *pam_error) +{ + const char *ntstatus = NULL; + const char *error_string = NULL; + + if (!e || !pam_error) { + return false; + } + + ntstatus = e->nt_string; + if (!ntstatus) { + return false; + } + + if (strcasecmp(ntstatus, nt_status_string) == 0) { + + error_string = _get_ntstatus_error_string(nt_status_string); + if (error_string) { + _make_remark(ctx, PAM_ERROR_MSG, error_string); + *pam_error = e->pam_error; + return true; + } + + if (e->display_string) { + _make_remark(ctx, PAM_ERROR_MSG, e->display_string); + *pam_error = e->pam_error; + return true; + } + + _make_remark(ctx, PAM_ERROR_MSG, nt_status_string); + *pam_error = e->pam_error; + + return true; + } + + return false; +}; + /** * Compose Password Restriction String for a PAM_ERROR_MSG conversation. * - * @param response The struct winbindd_response. + * @param i The wbcUserPasswordPolicyInfo struct. * - * @return string (caller needs to free). + * @return string (caller needs to talloc_free). */ -static char *_pam_compose_pwd_restriction_string(struct winbindd_response *response) +static char *_pam_compose_pwd_restriction_string(struct pwb_context *ctx, + struct wbcUserPasswordPolicyInfo *i) { char *str = NULL; - size_t offset = 0, ret = 0, str_size = 1024; - str = (char *)malloc(str_size); - if (!str) { - return NULL; + if (!i) { + goto failed; } - memset(str, '\0', str_size); - - offset = snprintf(str, str_size, "Your password "); - if (offset == -1) { + str = talloc_asprintf(ctx, "Your password "); + if (!str) { goto failed; } - if (response->data.auth.policy.min_length_password > 0) { - ret = snprintf(str+offset, str_size-offset, + if (i->min_length_password > 0) { + str = talloc_asprintf_append(str, "must be at least %d characters; ", - response->data.auth.policy.min_length_password); - if (ret == -1) { + i->min_length_password); + if (!str) { goto failed; } - offset += ret; } - if (response->data.auth.policy.password_history > 0) { - ret = snprintf(str+offset, str_size-offset, + if (i->password_history > 0) { + str = talloc_asprintf_append(str, "cannot repeat any of your previous %d " "passwords; ", - response->data.auth.policy.password_history); - if (ret == -1) { + i->password_history); + if (!str) { goto failed; } - offset += ret; } - if (response->data.auth.policy.password_properties & - DOMAIN_PASSWORD_COMPLEX) { - ret = snprintf(str+offset, str_size-offset, + if (i->password_properties & WBC_DOMAIN_PASSWORD_COMPLEX) { + str = talloc_asprintf_append(str, "must contain capitals, numerals " "or punctuation; " "and cannot contain your account " "or full name; "); - if (ret == -1) { + if (!str) { goto failed; } - offset += ret; } - ret = snprintf(str+offset, str_size-offset, + str = talloc_asprintf_append(str, "Please type a different password. " "Type a password which meets these requirements in " "both text boxes."); - if (ret == -1) { + if (!str) { goto failed; } return str; failed: - SAFE_FREE(str); + TALLOC_FREE(str); return NULL; } +static int _pam_create_homedir(struct pwb_context *ctx, + const char *dirname, + mode_t mode) +{ + struct stat sbuf; + + if (stat(dirname, &sbuf) == 0) { + return PAM_SUCCESS; + } + + if (mkdir(dirname, mode) != 0) { + + _make_remark_format(ctx, PAM_TEXT_INFO, + "Creating directory: %s failed: %s", + dirname, strerror(errno)); + _pam_log(ctx, LOG_ERR, "could not create dir: %s (%s)", + dirname, strerror(errno)); + return PAM_PERM_DENIED; + } + + return PAM_SUCCESS; +} + +static int _pam_chown_homedir(struct pwb_context *ctx, + const char *dirname, + uid_t uid, + gid_t gid) +{ + if (chown(dirname, uid, gid) != 0) { + _pam_log(ctx, LOG_ERR, "failed to chown user homedir: %s (%s)", + dirname, strerror(errno)); + return PAM_PERM_DENIED; + } + + return PAM_SUCCESS; +} + +static int _pam_mkhomedir(struct pwb_context *ctx) +{ + struct passwd *pwd = NULL; + char *token = NULL; + char *create_dir = NULL; + char *user_dir = NULL; + int ret; + const char *username; + mode_t mode = 0700; + char *safe_ptr = NULL; + char *p = NULL; + + /* Get the username */ + ret = pam_get_user(ctx->pamh, &username, NULL); + if ((ret != PAM_SUCCESS) || (!username)) { + _pam_log_debug(ctx, LOG_DEBUG, "can not get the username"); + return PAM_SERVICE_ERR; + } + + pwd = getpwnam(username); + if (pwd == NULL) { + _pam_log_debug(ctx, LOG_DEBUG, "can not get the username"); + return PAM_USER_UNKNOWN; + } + _pam_log_debug(ctx, LOG_DEBUG, "homedir is: %s", pwd->pw_dir); + + ret = _pam_create_homedir(ctx, pwd->pw_dir, 0700); + if (ret == PAM_SUCCESS) { + ret = _pam_chown_homedir(ctx, pwd->pw_dir, + pwd->pw_uid, + pwd->pw_gid); + } + + if (ret == PAM_SUCCESS) { + return ret; + } + + /* maybe we need to create parent dirs */ + create_dir = talloc_strdup(ctx, "/"); + if (!create_dir) { + return PAM_BUF_ERR; + } + + /* find final directory */ + user_dir = strrchr(pwd->pw_dir, '/'); + if (!user_dir) { + return PAM_BUF_ERR; + } + user_dir++; + + _pam_log(ctx, LOG_DEBUG, "final directory: %s", user_dir); + + p = pwd->pw_dir; + + while ((token = strtok_r(p, "/", &safe_ptr)) != NULL) { + + mode = 0755; + + p = NULL; + + _pam_log_debug(ctx, LOG_DEBUG, "token is %s", token); + + create_dir = talloc_asprintf_append(create_dir, "%s/", token); + if (!create_dir) { + return PAM_BUF_ERR; + } + _pam_log_debug(ctx, LOG_DEBUG, "current_dir is %s", create_dir); + + if (strcmp(token, user_dir) == 0) { + _pam_log_debug(ctx, LOG_DEBUG, "assuming last directory: %s", token); + mode = 0700; + } + + ret = _pam_create_homedir(ctx, create_dir, mode); + if (ret) { + return ret; + } + } + + return _pam_chown_homedir(ctx, create_dir, + pwd->pw_uid, + pwd->pw_gid); +} + /* talk to winbindd */ static int winbind_auth_request(struct pwb_context *ctx, const char *user, @@ -1297,37 +1512,52 @@ static int winbind_auth_request(struct pwb_context *ctx, const char *member, const char *cctype, const int warn_pwd_expire, - struct winbindd_response *p_response, + struct wbcAuthErrorInfo **p_error, + struct wbcLogonUserInfo **p_info, + struct wbcUserPasswordPolicyInfo **p_policy, time_t *pwd_last_set, char **user_ret) { - struct winbindd_request request; - struct winbindd_response response; - int ret; - bool already_expired = false; + wbcErr wbc_status; + + struct wbcLogonUserParams logon; + char membership_of[1024]; + uid_t user_uid = -1; + uint32_t flags = WBFLAG_PAM_INFO3_TEXT | + WBFLAG_PAM_GET_PWD_POLICY; - ZERO_STRUCT(request); - ZERO_STRUCT(response); + struct wbcLogonUserInfo *info = NULL; + struct wbcAuthUserInfo *user_info = NULL; + struct wbcAuthErrorInfo *error = NULL; + struct wbcUserPasswordPolicyInfo *policy = NULL; + + int ret = PAM_AUTH_ERR; + int i; + const char *codes[] = { + "NT_STATUS_PASSWORD_EXPIRED", + "NT_STATUS_PASSWORD_MUST_CHANGE", + "NT_STATUS_INVALID_WORKSTATION", + "NT_STATUS_INVALID_LOGON_HOURS", + "NT_STATUS_ACCOUNT_EXPIRED", + "NT_STATUS_ACCOUNT_DISABLED", + "NT_STATUS_ACCOUNT_LOCKED_OUT", + "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", + "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", + "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", + "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", + "NT_STATUS_NO_LOGON_SERVERS", + "NT_STATUS_WRONG_PASSWORD", + "NT_STATUS_ACCESS_DENIED" + }; if (pwd_last_set) { *pwd_last_set = 0; } - strncpy(request.data.auth.user, user, - sizeof(request.data.auth.user)-1); - - strncpy(request.data.auth.pass, pass, - sizeof(request.data.auth.pass)-1); - - request.data.auth.krb5_cc_type[0] = '\0'; - request.data.auth.uid = -1; - - request.flags = WBFLAG_PAM_INFO3_TEXT | WBFLAG_PAM_GET_PWD_POLICY; - /* Krb5 auth always has to go against the KDC of the user's realm */ if (ctx->ctrl & WINBIND_KRB5_AUTH) { - request.flags |= WBFLAG_PAM_CONTACT_TRUSTDOM; + flags |= WBFLAG_PAM_CONTACT_TRUSTDOM; } if (ctx->ctrl & (WINBIND_KRB5_AUTH|WINBIND_CACHED_LOGIN)) { @@ -1337,7 +1567,7 @@ static int winbind_auth_request(struct pwb_context *ctx, if (pwd == NULL) { return PAM_USER_UNKNOWN; } - request.data.auth.uid = pwd->pw_uid; + user_uid = pwd->pw_uid; } if (ctx->ctrl & WINBIND_KRB5_AUTH) { @@ -1345,38 +1575,34 @@ static int winbind_auth_request(struct pwb_context *ctx, _pam_log_debug(ctx, LOG_DEBUG, "enabling krb5 login flag\n"); - request.flags |= WBFLAG_PAM_KRB5 | - WBFLAG_PAM_FALLBACK_AFTER_KRB5; + flags |= WBFLAG_PAM_KRB5 | + WBFLAG_PAM_FALLBACK_AFTER_KRB5; } if (ctx->ctrl & WINBIND_CACHED_LOGIN) { _pam_log_debug(ctx, LOG_DEBUG, "enabling cached login flag\n"); - request.flags |= WBFLAG_PAM_CACHED_LOGIN; + flags |= WBFLAG_PAM_CACHED_LOGIN; } if (user_ret) { *user_ret = NULL; - request.flags |= WBFLAG_PAM_UNIX_NAME; + flags |= WBFLAG_PAM_UNIX_NAME; } if (cctype != NULL) { - strncpy(request.data.auth.krb5_cc_type, cctype, - sizeof(request.data.auth.krb5_cc_type) - 1); _pam_log_debug(ctx, LOG_DEBUG, "enabling request for a %s krb5 ccache\n", cctype); } - request.data.auth.require_membership_of_sid[0] = '\0'; - if (member != NULL) { - if (!winbind_name_list_to_sid_string_list(ctx, user, - member, - request.data.auth.require_membership_of_sid, - sizeof(request.data.auth.require_membership_of_sid))) { + ZERO_STRUCT(membership_of); + if (!winbind_name_list_to_sid_string_list(ctx, user, member, + membership_of, + sizeof(membership_of))) { _pam_log_debug(ctx, LOG_ERR, "failed to serialize membership of sid " "\"%s\"\n", member); @@ -1384,60 +1610,100 @@ static int winbind_auth_request(struct pwb_context *ctx, } } - ret = pam_winbind_request_log(ctx, WINBINDD_PAM_AUTH, - &request, &response, user); + ZERO_STRUCT(logon); - if (pwd_last_set) { - *pwd_last_set = response.data.auth.info3.pass_last_set_time; + logon.username = user; + logon.password = pass; + + wbc_status = wbcAddNamedBlob(&logon.num_blobs, + &logon.blobs, + "krb5_cc_type", + 0, + (uint8_t *)cctype, + strlen(cctype)+1); + if (!WBC_ERROR_IS_OK(wbc_status)) { + goto done; + } + + wbc_status = wbcAddNamedBlob(&logon.num_blobs, + &logon.blobs, + "flags", + 0, + (uint8_t *)&flags, + sizeof(flags)); + if (!WBC_ERROR_IS_OK(wbc_status)) { + goto done; + } + + wbc_status = wbcAddNamedBlob(&logon.num_blobs, + &logon.blobs, + "user_uid", + 0, + (uint8_t *)&user_uid, + sizeof(user_uid)); + if (!WBC_ERROR_IS_OK(wbc_status)) { + goto done; + } + + wbc_status = wbcAddNamedBlob(&logon.num_blobs, + &logon.blobs, + "membership_of", + 0, + (uint8_t *)membership_of, + sizeof(membership_of)); + if (!WBC_ERROR_IS_OK(wbc_status)) { + goto done; } - if (p_response) { - /* We want to process the response in the caller. */ - *p_response = response; + wbc_status = wbcLogonUser(&logon, &info, &error, &policy); + ret = wbc_auth_error_to_pam_error(ctx, error, wbc_status, + user, "wbcLogonUser"); + wbcFreeMemory(logon.blobs); + logon.blobs = NULL; + + if (info && info->info) { + user_info = info->info; + } + + if (pwd_last_set && user_info) { + *pwd_last_set = user_info->pass_last_set_time; + } + + if (p_info && info) { + *p_info = info; + } + + if (p_policy && policy) { + *p_policy = policy; + } + + if (p_error && error) { + /* We want to process the error in the caller. */ + *p_error = error; return ret; } - if (ret) { - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_PASSWORD_EXPIRED"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_PASSWORD_MUST_CHANGE"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_INVALID_WORKSTATION"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_INVALID_LOGON_HOURS"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_ACCOUNT_EXPIRED"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_ACCOUNT_DISABLED"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_ACCOUNT_LOCKED_OUT"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_NO_LOGON_SERVERS"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_WRONG_PASSWORD"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_ACCESS_DENIED"); + for (i=0; i<ARRAY_SIZE(codes); i++) { + int _ret = ret; + if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) { + ret = _ret; + goto done; + } } - if (ret == PAM_SUCCESS) { + if ((ret == PAM_SUCCESS) && user_info && policy && info) { + + bool already_expired = false; /* warn a user if the password is about to expire soon */ - _pam_warn_password_expiry(ctx, &response, + _pam_warn_password_expiry(ctx, user_info, policy, warn_pwd_expire, &already_expired); if (already_expired == true) { - SMB_TIME_T last_set; - last_set = response.data.auth.info3.pass_last_set_time; + + SMB_TIME_T last_set = user_info->pass_last_set_time; + _pam_log_debug(ctx, LOG_DEBUG, "Password has expired " "(Password was last set: %lld, " @@ -1445,33 +1711,44 @@ static int winbind_auth_request(struct pwb_context *ctx, "%lld (now it's: %lu))\n", (long long int)last_set, (long long int)last_set + - response.data.auth.policy.expire, + policy->expire, time(NULL)); return PAM_AUTHTOK_EXPIRED; } /* inform about logon type */ - _pam_warn_logon_type(ctx, user, - response.data.auth.info3.user_flgs); + _pam_warn_logon_type(ctx, user, user_info->user_flags); /* inform about krb5 failures */ - _pam_warn_krb5_failure(ctx, user, - response.data.auth.info3.user_flgs); + _pam_warn_krb5_failure(ctx, user, user_info->user_flags); /* set some info3 info for other modules in the stack */ - _pam_set_data_info3(ctx, &response); + _pam_set_data_info3(ctx, user_info); /* put krb5ccname into env */ - _pam_setup_krb5_env(ctx, response.data.auth.krb5ccname); + _pam_setup_krb5_env(ctx, info); /* If winbindd returned a username, return the pointer to it * here. */ - if (user_ret && response.data.auth.unix_username[0]) { - /* We have to trust it's a null terminated string. */ - *user_ret = strndup(response.data.auth.unix_username, - sizeof(response.data.auth.unix_username) - 1); - } + _pam_setup_unix_username(ctx, user_ret, info); + } + + done: + if (logon.blobs) { + wbcFreeMemory(logon.blobs); + } + if (info && info->blobs) { + wbcFreeMemory(info->blobs); + } + if (error && !p_error) { + wbcFreeMemory(error); + } + if (info && !p_info) { + wbcFreeMemory(info); + } + if (policy && !p_policy) { + wbcFreeMemory(policy); } return ret; @@ -1484,99 +1761,95 @@ static int winbind_chauthtok_request(struct pwb_context *ctx, const char *newpass, time_t pwd_last_set) { - struct winbindd_request request; - struct winbindd_response response; - int ret; + wbcErr wbc_status; + struct wbcChangePasswordParams params; + struct wbcAuthErrorInfo *error = NULL; + struct wbcUserPasswordPolicyInfo *policy = NULL; + enum wbcPasswordChangeRejectReason reject_reason = -1; + uint32_t flags = 0; - ZERO_STRUCT(request); - ZERO_STRUCT(response); + int i; + const char *codes[] = { + "NT_STATUS_BACKUP_CONTROLLER", + "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", + "NT_STATUS_NO_LOGON_SERVERS", + "NT_STATUS_ACCESS_DENIED", + "NT_STATUS_PWD_TOO_SHORT", /* TODO: tell the min pwd length ? */ + "NT_STATUS_PWD_TOO_RECENT", /* TODO: tell the minage ? */ + "NT_STATUS_PWD_HISTORY_CONFLICT" /* TODO: tell the history length ? */ + }; + int ret = PAM_AUTH_ERR; + + ZERO_STRUCT(params); - if (request.data.chauthtok.user == NULL) { - return -2; + if (ctx->ctrl & WINBIND_KRB5_AUTH) { + flags |= WBFLAG_PAM_KRB5 | + WBFLAG_PAM_CONTACT_TRUSTDOM; } - strncpy(request.data.chauthtok.user, user, - sizeof(request.data.chauthtok.user) - 1); - - if (oldpass != NULL) { - strncpy(request.data.chauthtok.oldpass, oldpass, - sizeof(request.data.chauthtok.oldpass) - 1); - } else { - request.data.chauthtok.oldpass[0] = '\0'; + if (ctx->ctrl & WINBIND_CACHED_LOGIN) { + flags |= WBFLAG_PAM_CACHED_LOGIN; } - if (newpass != NULL) { - strncpy(request.data.chauthtok.newpass, newpass, - sizeof(request.data.chauthtok.newpass) - 1); - } else { - request.data.chauthtok.newpass[0] = '\0'; - } + params.account_name = user; + params.level = WBC_AUTH_USER_LEVEL_PLAIN; + params.old_password.plaintext = oldpass; + params.new_password.plaintext = newpass; + params.flags = flags; - if (ctx->ctrl & WINBIND_KRB5_AUTH) { - request.flags = WBFLAG_PAM_KRB5 | - WBFLAG_PAM_CONTACT_TRUSTDOM; - } + wbc_status = wbcChangeUserPasswordEx(¶ms, &error, &reject_reason, &policy); + ret = wbc_auth_error_to_pam_error(ctx, error, wbc_status, + user, "wbcChangeUserPasswordEx"); - if (ctx->ctrl & WINBIND_CACHED_LOGIN) { - request.flags |= WBFLAG_PAM_CACHED_LOGIN; + if (WBC_ERROR_IS_OK(wbc_status)) { + _pam_log(ctx, LOG_NOTICE, + "user '%s' password changed", user); + return PAM_SUCCESS; } - ret = pam_winbind_request_log(ctx, WINBINDD_PAM_CHAUTHTOK, - &request, &response, user); - - if (ret == PAM_SUCCESS) { + if (!error) { + wbcFreeMemory(policy); return ret; } - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_BACKUP_CONTROLLER"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_NO_LOGON_SERVERS"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_ACCESS_DENIED"); - - /* TODO: tell the min pwd length ? */ - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_PWD_TOO_SHORT"); - - /* TODO: tell the minage ? */ - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_PWD_TOO_RECENT"); - - /* TODO: tell the history length ? */ - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_PWD_HISTORY_CONFLICT"); + for (i=0; i<ARRAY_SIZE(codes); i++) { + int _ret = ret; + if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) { + ret = _ret; + goto done; + } + } - if (!strcasecmp(response.data.auth.nt_status_string, + if (!strcasecmp(error->nt_string, "NT_STATUS_PASSWORD_RESTRICTION")) { char *pwd_restriction_string = NULL; - SMB_TIME_T min_pwd_age; - uint32_t reject_reason = response.data.auth.reject_reason; - min_pwd_age = response.data.auth.policy.min_passwordage; + SMB_TIME_T min_pwd_age = 0; + + if (policy) { + min_pwd_age = policy->min_passwordage; + } /* FIXME: avoid to send multiple PAM messages after another */ switch (reject_reason) { case -1: break; - case SAMR_REJECT_OTHER: + case WBC_PWD_CHANGE_REJECT_OTHER: if ((min_pwd_age > 0) && (pwd_last_set + min_pwd_age > time(NULL))) { PAM_WB_REMARK_DIRECT(ctx, "NT_STATUS_PWD_TOO_RECENT"); } break; - case SAMR_REJECT_TOO_SHORT: + case WBC_PWD_CHANGE_REJECT_TOO_SHORT: PAM_WB_REMARK_DIRECT(ctx, "NT_STATUS_PWD_TOO_SHORT"); break; - case SAMR_REJECT_IN_HISTORY: + case WBC_PWD_CHANGE_REJECT_IN_HISTORY: PAM_WB_REMARK_DIRECT(ctx, "NT_STATUS_PWD_HISTORY_CONFLICT"); break; - case SAMR_REJECT_COMPLEXITY: + case WBC_PWD_CHANGE_REJECT_COMPLEXITY: _make_remark(ctx, PAM_ERROR_MSG, "Password does not meet " "complexity requirements"); @@ -1590,13 +1863,16 @@ static int winbind_chauthtok_request(struct pwb_context *ctx, } pwd_restriction_string = - _pam_compose_pwd_restriction_string(&response); + _pam_compose_pwd_restriction_string(ctx, policy); if (pwd_restriction_string) { _make_remark(ctx, PAM_ERROR_MSG, pwd_restriction_string); - SAFE_FREE(pwd_restriction_string); + TALLOC_FREE(pwd_restriction_string); } } + done: + wbcFreeMemory(error); + wbcFreeMemory(policy); return ret; } @@ -1616,29 +1892,26 @@ static int valid_user(struct pwb_context *ctx, * sure it's really a winbind user, this is important when stacking PAM * modules in the 'account' or 'password' facility. */ + wbcErr wbc_status; struct passwd *pwd = NULL; - struct winbindd_request request; - struct winbindd_response response; - int ret; - - ZERO_STRUCT(request); - ZERO_STRUCT(response); + struct passwd *wb_pwd = NULL; pwd = getpwnam(user); if (pwd == NULL) { return 1; } - strncpy(request.data.username, user, - sizeof(request.data.username) - 1); - - ret = pam_winbind_request_log(ctx, WINBINDD_GETPWNAM, - &request, &response, user); + wbc_status = wbcGetpwnam(user, &wb_pwd); + wbcFreeMemory(wb_pwd); + if (!WBC_ERROR_IS_OK(wbc_status)) { + _pam_log(ctx, LOG_DEBUG, "valid_user: wbcGetpwnam gave %s\n", + wbcErrorString(wbc_status)); + } - switch (ret) { - case PAM_USER_UNKNOWN: + switch (wbc_status) { + case WBC_ERR_UNKNOWN_USER: return 1; - case PAM_SUCCESS: + case WBC_ERR_SUCCESS: return 0; default: break; @@ -1846,12 +2119,13 @@ static const char *get_conf_item_string(struct pwb_context *ctx, if (ctx->dict) { char *key = NULL; - if (!asprintf(&key, "global:%s", item)) { + key = talloc_asprintf(ctx, "global:%s", item); + if (!key) { goto out; } parm_opt = iniparser_getstr(ctx->dict, key); - SAFE_FREE(key); + TALLOC_FREE(key); _pam_log_debug(ctx, LOG_INFO, "CONFIG file: %s '%s'\n", item, parm_opt); @@ -1893,12 +2167,13 @@ static int get_config_item_int(struct pwb_context *ctx, if (ctx->dict) { char *key = NULL; - if (!asprintf(&key, "global:%s", item)) { + key = talloc_asprintf(ctx, "global:%s", item); + if (!key) { goto out; } parm_opt = iniparser_getint(ctx->dict, key, -1); - SAFE_FREE(key); + TALLOC_FREE(key); _pam_log_debug(ctx, LOG_INFO, "CONFIG file: %s '%d'\n", @@ -1948,20 +2223,25 @@ static int get_warn_pwd_expire_from_config(struct pwb_context *ctx) static char winbind_get_separator(struct pwb_context *ctx) { - struct winbindd_request request; - struct winbindd_response response; + wbcErr wbc_status; + static struct wbcInterfaceDetails *details = NULL; - ZERO_STRUCT(request); - ZERO_STRUCT(response); + wbc_status = wbcInterfaceDetails(&details); + if (!WBC_ERROR_IS_OK(wbc_status)) { + _pam_log(ctx, LOG_ERR, + "Could not retrieve winbind interface details: %s", + wbcErrorString(wbc_status)); + return '\0'; + } - if (pam_winbind_request_log(ctx, WINBINDD_INFO, - &request, &response, NULL)) { + if (!details) { return '\0'; } - return response.data.info.winbind_separator; + return details->winbind_separator; } + /** * Convert a upn to a name. * @@ -1974,12 +2254,12 @@ static char winbind_get_separator(struct pwb_context *ctx) static char* winbind_upn_to_username(struct pwb_context *ctx, const char *upn) { - struct winbindd_request req; - struct winbindd_response resp; - int retval; - char *account_name; - int account_name_len; char sep; + wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE; + struct wbcDomainSid sid; + enum wbcSidType type; + char *domain; + char *name; /* This cannot work when the winbind separator = @ */ @@ -1990,35 +2270,19 @@ static char* winbind_upn_to_username(struct pwb_context *ctx, /* Convert the UPN to a SID */ - ZERO_STRUCT(req); - ZERO_STRUCT(resp); - - strncpy(req.data.name.dom_name, "", - sizeof(req.data.name.dom_name) - 1); - strncpy(req.data.name.name, upn, - sizeof(req.data.name.name) - 1); - retval = pam_winbind_request_log(ctx, WINBINDD_LOOKUPNAME, - &req, &resp, upn); - if (retval != PAM_SUCCESS) { + wbc_status = wbcLookupName("", upn, &sid, &type); + if (!WBC_ERROR_IS_OK(wbc_status)) { return NULL; } /* Convert the the SID back to the sAMAccountName */ - ZERO_STRUCT(req); - strncpy(req.data.sid, resp.data.sid.sid, sizeof(req.data.sid)-1); - ZERO_STRUCT(resp); - retval = pam_winbind_request_log(ctx, WINBINDD_LOOKUPSID, - &req, &resp, upn); - if (retval != PAM_SUCCESS) { + wbc_status = wbcLookupSid(&sid, &domain, &name, &type); + if (!WBC_ERROR_IS_OK(wbc_status)) { return NULL; } - account_name_len = asprintf(&account_name, "%s\\%s", - resp.data.name.dom_name, - resp.data.name.name); - - return account_name; + return talloc_asprintf(ctx, "%s\\%s", domain, name); } PAM_EXTERN @@ -2087,7 +2351,7 @@ int pam_sm_authenticate(pam_handle_t *pamh, int flags, real_username); if (samaccountname) { free(real_username); - real_username = samaccountname; + real_username = strdup(samaccountname); } } @@ -2119,7 +2383,8 @@ int pam_sm_authenticate(pam_handle_t *pamh, int flags, /* Now use the username to look up password */ retval = winbind_auth_request(ctx, real_username, password, - member, cctype, warn_pwd_expire, NULL, + member, cctype, warn_pwd_expire, + NULL, NULL, NULL, NULL, &username_ret); if (retval == PAM_NEW_AUTHTOK_REQD || @@ -2127,7 +2392,8 @@ int pam_sm_authenticate(pam_handle_t *pamh, int flags, char *new_authtok_required_during_auth = NULL; - if (!asprintf(&new_authtok_required, "%d", retval)) { + new_authtok_required = talloc_asprintf(NULL, "%d", retval); + if (!new_authtok_required) { retval = PAM_BUF_ERR; goto out; } @@ -2138,7 +2404,8 @@ int pam_sm_authenticate(pam_handle_t *pamh, int flags, retval = PAM_SUCCESS; - if (!asprintf(&new_authtok_required_during_auth, "%d", true)) { + new_authtok_required_during_auth = talloc_asprintf(NULL, "%d", true); + if (!new_authtok_required_during_auth) { retval = PAM_BUF_ERR; goto out; } @@ -2172,7 +2439,7 @@ out: _PAM_LOG_FUNCTION_LEAVE("pam_sm_authenticate", ctx, retval); - _pam_winbind_free_context(ctx); + TALLOC_FREE(ctx); return retval; } @@ -2220,7 +2487,7 @@ int pam_sm_setcred(pam_handle_t *pamh, int flags, _PAM_LOG_FUNCTION_LEAVE("pam_sm_setcred", ctx, ret); - _pam_winbind_free_context(ctx); + TALLOC_FREE(ctx); return ret; } @@ -2321,7 +2588,7 @@ int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, _PAM_LOG_FUNCTION_LEAVE("pam_sm_acct_mgmt", ctx, ret); - _pam_winbind_free_context(ctx); + TALLOC_FREE(ctx); return ret; } @@ -2330,7 +2597,7 @@ PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) { - int ret = PAM_SYSTEM_ERR; + int ret = PAM_SUCCESS; struct pwb_context *ctx = NULL; ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx); @@ -2340,12 +2607,14 @@ int pam_sm_open_session(pam_handle_t *pamh, int flags, _PAM_LOG_FUNCTION_ENTER("pam_sm_open_session", ctx); - ret = PAM_SUCCESS; - + if (ctx->ctrl & WINBIND_MKHOMEDIR) { + /* check and create homedir */ + ret = _pam_mkhomedir(ctx); + } out: _PAM_LOG_FUNCTION_LEAVE("pam_sm_open_session", ctx, ret); - _pam_winbind_free_context(ctx); + TALLOC_FREE(ctx); return ret; } @@ -2356,6 +2625,7 @@ int pam_sm_close_session(pam_handle_t *pamh, int flags, { int retval = PAM_SUCCESS; struct pwb_context *ctx = NULL; + struct wbcLogoffUserParams logoff; retval = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx); if (retval) { @@ -2372,15 +2642,15 @@ int pam_sm_close_session(pam_handle_t *pamh, int flags, if (ctx->ctrl & WINBIND_KRB5_AUTH) { /* destroy the ccache here */ - struct winbindd_request request; - struct winbindd_response response; + + wbcErr wbc_status; + struct wbcAuthErrorInfo *error = NULL; + + uint32_t flags = 0; const char *user; const char *ccname = NULL; struct passwd *pwd = NULL; - ZERO_STRUCT(request); - ZERO_STRUCT(response); - retval = pam_get_user(pamh, &user, "Username: "); if (retval) { _pam_log(ctx, LOG_ERR, @@ -2404,34 +2674,70 @@ int pam_sm_close_session(pam_handle_t *pamh, int flags, "user has no KRB5CCNAME environment"); } - strncpy(request.data.logoff.user, user, - sizeof(request.data.logoff.user) - 1); - - if (ccname) { - strncpy(request.data.logoff.krb5ccname, ccname, - sizeof(request.data.logoff.krb5ccname) - 1); - } - pwd = getpwnam(user); if (pwd == NULL) { retval = PAM_USER_UNKNOWN; goto out; } - request.data.logoff.uid = pwd->pw_uid; - request.flags = WBFLAG_PAM_KRB5 | - WBFLAG_PAM_CONTACT_TRUSTDOM; + flags = WBFLAG_PAM_KRB5 | + WBFLAG_PAM_CONTACT_TRUSTDOM; + + ZERO_STRUCT(logoff); + + logoff.username = user; - retval = pam_winbind_request_log(ctx, - WINBINDD_PAM_LOGOFF, - &request, &response, user); + wbc_status = wbcAddNamedBlob(&logoff.num_blobs, + &logoff.blobs, + "ccfilename", + 0, + (uint8_t *)ccname, + strlen(ccname)+1); + if (!WBC_ERROR_IS_OK(wbc_status)) { + goto out; + } + + wbc_status = wbcAddNamedBlob(&logoff.num_blobs, + &logoff.blobs, + "flags", + 0, + (uint8_t *)&flags, + sizeof(flags)); + if (!WBC_ERROR_IS_OK(wbc_status)) { + goto out; + } + + wbc_status = wbcAddNamedBlob(&logoff.num_blobs, + &logoff.blobs, + "user_uid", + 0, + (uint8_t *)&pwd->pw_uid, + sizeof(pwd->pw_uid)); + if (!WBC_ERROR_IS_OK(wbc_status)) { + goto out; + } + + wbc_status = wbcLogoffUserEx(&logoff, &error); + retval = wbc_auth_error_to_pam_error(ctx, error, wbc_status, + user, "wbcLogoffUser"); + wbcFreeMemory(error); + wbcFreeMemory(logoff.blobs); + + if (!WBC_ERROR_IS_OK(wbc_status)) { + _pam_log(ctx, LOG_INFO, + "failed to logoff user %s: %s\n", + user, wbcErrorString(wbc_status)); + } } out: + if (logoff.blobs) { + wbcFreeMemory(logoff.blobs); + } _PAM_LOG_FUNCTION_LEAVE("pam_sm_close_session", ctx, retval); - _pam_winbind_free_context(ctx); + TALLOC_FREE(ctx); return retval; } @@ -2503,11 +2809,9 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int retry = 0; char *username_ret = NULL; - struct winbindd_response response; + struct wbcAuthErrorInfo *error = NULL; struct pwb_context *ctx = NULL; - ZERO_STRUCT(response); - ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx); if (ret) { goto out; @@ -2560,16 +2864,15 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, time_t pwdlastset_prelim = 0; /* instruct user what is happening */ -#define greeting "Changing password for " - Announce = (char *) malloc(sizeof(greeting) + strlen(user)); - if (Announce == NULL) { + +#define greeting "Changing password for" + Announce = talloc_asprintf(ctx, "%s %s", greeting, user); + if (!Announce) { _pam_log(ctx, LOG_CRIT, "password - out of memory"); ret = PAM_BUF_ERR; goto out; } - (void) strcpy(Announce, greeting); - (void) strcpy(Announce + sizeof(greeting) - 1, user); #undef greeting lctrl = ctx->ctrl | WINBIND__OLD_PASSWORD; @@ -2578,6 +2881,7 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, "(current) NT password: ", NULL, (const char **) &pass_old); + TALLOC_FREE(Announce); if (ret != PAM_SUCCESS) { _pam_log(ctx, LOG_NOTICE, "password - (old) token not obtained"); @@ -2587,7 +2891,8 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, /* verify that this is the password for this user */ ret = winbind_auth_request(ctx, user, pass_old, - NULL, NULL, 0, &response, + NULL, NULL, 0, + &error, NULL, NULL, &pwdlastset_prelim, NULL); if (ret != PAM_ACCT_EXPIRED && @@ -2696,6 +3001,8 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, const char *member = NULL; const char *cctype = NULL; int warn_pwd_expire; + struct wbcLogonUserInfo *info = NULL; + struct wbcUserPasswordPolicyInfo *policy = NULL; member = get_member_from_config(ctx); cctype = get_krb5_cc_type_from_config(ctx); @@ -2710,7 +3017,8 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, * */ ret = winbind_auth_request(ctx, user, pass_new, - member, cctype, 0, &response, + member, cctype, 0, + &error, &info, &policy, NULL, &username_ret); _pam_overwrite(pass_new); _pam_overwrite(pass_old); @@ -2718,19 +3026,24 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, if (ret == PAM_SUCCESS) { + struct wbcAuthUserInfo *user_info = NULL; + + if (info && info->info) { + user_info = info->info; + } + /* warn a user if the password is about to * expire soon */ - _pam_warn_password_expiry(ctx, &response, + _pam_warn_password_expiry(ctx, user_info, policy, warn_pwd_expire, NULL); /* set some info3 info for other modules in the * stack */ - _pam_set_data_info3(ctx, &response); + _pam_set_data_info3(ctx, user_info); /* put krb5ccname into env */ - _pam_setup_krb5_env(ctx, - response.data.auth.krb5ccname); + _pam_setup_krb5_env(ctx, info); if (username_ret) { pam_set_item(pamh, PAM_USER, @@ -2740,6 +3053,9 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, username_ret); free(username_ret); } + + wbcFreeMemory(info); + wbcFreeMemory(policy); } goto out; @@ -2749,18 +3065,28 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, } out: + { + /* Deal with offline errors. */ + int i; + const char *codes[] = { + "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", + "NT_STATUS_NO_LOGON_SERVERS", + "NT_STATUS_ACCESS_DENIED" + }; + + for (i=0; i<ARRAY_SIZE(codes); i++) { + int _ret; + if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) { + break; + } + } + } - /* Deal with offline errors. */ - PAM_WB_REMARK_CHECK_RESPONSE(ctx, response, - "NT_STATUS_NO_LOGON_SERVERS"); - PAM_WB_REMARK_CHECK_RESPONSE(ctx, response, - "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND"); - PAM_WB_REMARK_CHECK_RESPONSE(ctx, response, - "NT_STATUS_ACCESS_DENIED"); + wbcFreeMemory(error); _PAM_LOG_FUNCTION_LEAVE("pam_sm_chauthtok", ctx, ret); - _pam_winbind_free_context(ctx); + TALLOC_FREE(ctx); return ret; } diff --git a/source3/nsswitch/pam_winbind.h b/source3/nsswitch/pam_winbind.h index 6dba127cdc..cb6f450ccb 100644 --- a/source3/nsswitch/pam_winbind.h +++ b/source3/nsswitch/pam_winbind.h @@ -7,6 +7,8 @@ #include "../lib/replace/replace.h" #include "system/syslog.h" #include "system/time.h" +#include <talloc.h> +#include "libwbclient/wbclient.h" #define MODULE_NAME "pam_winbind" #define PAM_SM_AUTH @@ -97,6 +99,7 @@ do { \ #define WINBIND_SILENT 0x00000800 #define WINBIND_DEBUG_STATE 0x00001000 #define WINBIND_WARN_PWD_EXPIRE 0x00002000 +#define WINBIND_MKHOMEDIR 0x00004000 /* * here is the string to inform the user that the new passwords they @@ -133,73 +136,11 @@ do { \ };\ }; -#define PAM_WB_REMARK_DIRECT_RET(h,f,x)\ -{\ - const char *error_string = NULL; \ - error_string = _get_ntstatus_error_string(x);\ - if (error_string != NULL) {\ - _make_remark(h, f, PAM_ERROR_MSG, error_string);\ - return ret;\ - };\ - _make_remark(h, f, PAM_ERROR_MSG, x);\ - return ret;\ -}; - -#define PAM_WB_REMARK_CHECK_RESPONSE(c,x,y)\ -{\ - const char *ntstatus = x.data.auth.nt_status_string; \ - const char *error_string = NULL; \ - if (!strcasecmp(ntstatus,y)) {\ - error_string = _get_ntstatus_error_string(y);\ - if (error_string != NULL) {\ - _make_remark(c, PAM_ERROR_MSG, error_string);\ - };\ - if (x.data.auth.error_string[0] != '\0') {\ - _make_remark(c, PAM_ERROR_MSG, x.data.auth.error_string);\ - };\ - _make_remark(c, PAM_ERROR_MSG, y);\ - };\ -}; - -#define PAM_WB_REMARK_CHECK_RESPONSE_RET(c,x,y)\ -{\ - const char *ntstatus = x.data.auth.nt_status_string; \ - const char *error_string = NULL; \ - if (!strcasecmp(ntstatus,y)) {\ - error_string = _get_ntstatus_error_string(y);\ - if (error_string != NULL) {\ - _make_remark(c, PAM_ERROR_MSG, error_string);\ - return ret;\ - };\ - if (x.data.auth.error_string[0] != '\0') {\ - _make_remark(c, PAM_ERROR_MSG, x.data.auth.error_string);\ - return ret;\ - };\ - _make_remark(c, PAM_ERROR_MSG, y);\ - return ret;\ - };\ -}; - -/* from samr.idl */ -#define DOMAIN_PASSWORD_COMPLEX 0x00000001 - -#define SAMR_REJECT_OTHER 0x00000000 -#define SAMR_REJECT_TOO_SHORT 0x00000001 -#define SAMR_REJECT_IN_HISTORY 0x00000002 -#define SAMR_REJECT_COMPLEXITY 0x00000005 - -#define ACB_PWNOEXP 0x00000200 - -/* from netlogon.idl */ -#define NETLOGON_CACHED_ACCOUNT 0x00000004 -#define NETLOGON_GRACE_LOGON 0x01000000 - -/* from include/rpc_netlogon.h */ #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000 -#define PAM_WB_CACHED_LOGON(x) (x & NETLOGON_CACHED_ACCOUNT) +#define PAM_WB_CACHED_LOGON(x) (x & WBC_AUTH_USER_INFO_CACHED_ACCOUNT) #define PAM_WB_KRB5_CLOCK_SKEW(x) (x & LOGON_KRB5_FAIL_CLOCK_SKEW) -#define PAM_WB_GRACE_LOGON(x) ((NETLOGON_CACHED_ACCOUNT|NETLOGON_GRACE_LOGON) == ( x & (NETLOGON_CACHED_ACCOUNT|NETLOGON_GRACE_LOGON))) +#define PAM_WB_GRACE_LOGON(x) ((WBC_AUTH_USER_INFO_CACHED_ACCOUNT|WBC_AUTH_USER_INFO_GRACE_LOGON) == ( x & (WBC_AUTH_USER_INFO_CACHED_ACCOUNT|WBC_AUTH_USER_INFO_GRACE_LOGON))) struct pwb_context { pam_handle_t *pamh; @@ -209,3 +150,8 @@ struct pwb_context { dictionary *dict; uint32_t ctrl; }; + +#define TALLOC_FREE(ctx) do { if ((ctx) != NULL) {talloc_free(ctx); ctx=NULL;} } while(0) +#define TALLOC_ZERO_P(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type) +#define TALLOC_P(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type) + diff --git a/source3/nsswitch/wbinfo.c b/source3/nsswitch/wbinfo.c index 60524d1d1b..fc49be4314 100644 --- a/source3/nsswitch/wbinfo.c +++ b/source3/nsswitch/wbinfo.c @@ -538,8 +538,8 @@ static bool wbinfo_dsgetdcname(const char *domain_name, uint32_t flags) ZERO_STRUCT(request); ZERO_STRUCT(response); - fstrcpy(request.domain_name, domain_name); - request.flags = flags; + fstrcpy(request.data.dsgetdcname.domain_name, domain_name); + request.data.dsgetdcname.flags = flags; request.flags |= DS_DIRECTORY_SERVICE_REQUIRED; @@ -553,7 +553,15 @@ static bool wbinfo_dsgetdcname(const char *domain_name, uint32_t flags) /* Display response */ - d_printf("%s\n", response.data.dc_name); + d_printf("%s\n", response.data.dsgetdcname.dc_unc); + d_printf("%s\n", response.data.dsgetdcname.dc_address); + d_printf("%d\n", response.data.dsgetdcname.dc_address_type); + d_printf("%s\n", response.data.dsgetdcname.domain_guid); + d_printf("%s\n", response.data.dsgetdcname.domain_name); + d_printf("%s\n", response.data.dsgetdcname.forest_name); + d_printf("0x%08x\n", response.data.dsgetdcname.dc_flags); + d_printf("%s\n", response.data.dsgetdcname.dc_site_name); + d_printf("%s\n", response.data.dsgetdcname.client_site_name); return true; } diff --git a/source3/nsswitch/winbind_krb5_locator.c b/source3/nsswitch/winbind_krb5_locator.c index 990c2cae50..b9e35bdec5 100644 --- a/source3/nsswitch/winbind_krb5_locator.c +++ b/source3/nsswitch/winbind_krb5_locator.c @@ -1,7 +1,7 @@ /* Unix SMB/CIFS implementation. kerberos locator plugin - Copyright (C) Guenther Deschner 2007 + Copyright (C) Guenther Deschner 2007-2008 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 @@ -18,6 +18,7 @@ */ #include "nsswitch/winbind_client.h" +#include "libwbclient/wbclient.h" #ifndef DEBUG_KRB5 #undef DEBUG_KRB5 @@ -244,37 +245,50 @@ static void smb_krb5_locator_close(void *private_data) static bool ask_winbind(const char *realm, char **dcname) { - NSS_STATUS status; - struct winbindd_request request; - struct winbindd_response response; + wbcErr wbc_status; + const char *dc = NULL; + struct wbcDomainControllerInfoEx *dc_info = NULL; + uint32_t flags; - ZERO_STRUCT(request); - ZERO_STRUCT(response); + flags = WBC_LOOKUP_DC_KDC_REQUIRED | + WBC_LOOKUP_DC_IS_DNS_NAME | + WBC_LOOKUP_DC_RETURN_DNS_NAME | + WBC_LOOKUP_DC_IP_REQUIRED; - request.flags = 0x40020600; - /* DS_KDC_REQUIRED | - DS_IS_DNS_NAME | - DS_RETURN_DNS_NAME | - DS_IP_REQUIRED */ + wbc_status = wbcLookupDomainControllerEx(realm, NULL, NULL, flags, &dc_info); - strncpy(request.domain_name, realm, - sizeof(request.domain_name)-1); - - status = winbindd_request_response(WINBINDD_DSGETDCNAME, - &request, &response); - if (status != NSS_STATUS_SUCCESS) { + if (!WBC_ERROR_IS_OK(wbc_status)) { #ifdef DEBUG_KRB5 fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: failed with: %s\n", - (unsigned int)getpid(), nss_err_str(status)); + (unsigned int)getpid(), wbcErrorString(wbc_status)); #endif return false; } - *dcname = strdup(response.data.dc_name); + if (dc_info->dc_address) { + dc = dc_info->dc_address; + if (dc[0] == '\\') dc++; + if (dc[0] == '\\') dc++; + } + + if (!dc && dc_info->dc_unc) { + dc = dc_info->dc_unc; + if (dc[0] == '\\') dc++; + if (dc[0] == '\\') dc++; + } + + if (!dc) { + wbcFreeMemory(dc_info); + return false; + } + + *dcname = strdup(dc); if (!*dcname) { + wbcFreeMemory(dc_info); return false; } + wbcFreeMemory(dc_info); return true; } diff --git a/source3/nsswitch/winbind_struct_protocol.h b/source3/nsswitch/winbind_struct_protocol.h index e81813c77b..ff52dbddaf 100644 --- a/source3/nsswitch/winbind_struct_protocol.h +++ b/source3/nsswitch/winbind_struct_protocol.h @@ -313,6 +313,12 @@ struct winbindd_request { uint32_t initial_blob_len; /* blobs in extra_data */ uint32_t challenge_blob_len; } ccache_ntlm_auth; + struct { + fstring domain_name; + fstring domain_guid; + fstring site_name; + uint32_t flags; + } dsgetdcname; /* padding -- needed to fix alignment between 32bit and 64bit libs. The size is the sizeof the union without the padding aligned on @@ -453,6 +459,17 @@ struct winbindd_response { struct { uint32_t auth_blob_len; /* blob in extra_data */ } ccache_ntlm_auth; + struct { + fstring dc_unc; + fstring dc_address; + uint32_t dc_address_type; + fstring domain_guid; + fstring domain_name; + fstring forest_name; + uint32_t dc_flags; + fstring dc_site_name; + fstring client_site_name; + } dsgetdcname; } data; /* Variable length return data */ |