LCOV - code coverage report
Current view: top level - nsswitch - pam_winbind.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-test 1498b464 Lines: 644 1419 45.4 %
Date: 2024-06-13 04:01:37 Functions: 44 57 77.2 %

          Line data    Source code
       1             : /* pam_winbind module
       2             : 
       3             :    Copyright Andrew Tridgell <tridge@samba.org> 2000
       4             :    Copyright Tim Potter <tpot@samba.org> 2000
       5             :    Copyright Andrew Bartlett <abartlet@samba.org> 2002
       6             :    Copyright Guenther Deschner <gd@samba.org> 2005-2008
       7             : 
       8             :    largely based on pam_userdb by Cristian Gafton <gafton@redhat.com> also
       9             :    contains large slabs of code from pam_unix by Elliot Lee
      10             :    <sopwith@redhat.com> (see copyright below for full details)
      11             : */
      12             : 
      13             : #include "pam_winbind.h"
      14             : 
      15             : enum pam_winbind_request_type
      16             : {
      17             :         PAM_WINBIND_AUTHENTICATE,
      18             :         PAM_WINBIND_SETCRED,
      19             :         PAM_WINBIND_ACCT_MGMT,
      20             :         PAM_WINBIND_OPEN_SESSION,
      21             :         PAM_WINBIND_CLOSE_SESSION,
      22             :         PAM_WINBIND_CHAUTHTOK,
      23             :         PAM_WINBIND_CLEANUP
      24             : };
      25             : 
      26           0 : static int wbc_error_to_pam_error(wbcErr status)
      27             : {
      28           0 :         switch (status) {
      29           0 :                 case WBC_ERR_SUCCESS:
      30           0 :                         return PAM_SUCCESS;
      31           0 :                 case WBC_ERR_NOT_IMPLEMENTED:
      32           0 :                         return PAM_SERVICE_ERR;
      33           0 :                 case WBC_ERR_UNKNOWN_FAILURE:
      34           0 :                         break;
      35           0 :                 case WBC_ERR_NO_MEMORY:
      36           0 :                         return PAM_BUF_ERR;
      37           0 :                 case WBC_ERR_INVALID_SID:
      38             :                 case WBC_ERR_INVALID_PARAM:
      39           0 :                         break;
      40           0 :                 case WBC_ERR_WINBIND_NOT_AVAILABLE:
      41           0 :                         return PAM_AUTHINFO_UNAVAIL;
      42           0 :                 case WBC_ERR_DOMAIN_NOT_FOUND:
      43           0 :                         return PAM_AUTHINFO_UNAVAIL;
      44           0 :                 case WBC_ERR_INVALID_RESPONSE:
      45           0 :                         return PAM_BUF_ERR;
      46           0 :                 case WBC_ERR_NSS_ERROR:
      47           0 :                         return PAM_USER_UNKNOWN;
      48           0 :                 case WBC_ERR_AUTH_ERROR:
      49           0 :                         return PAM_AUTH_ERR;
      50           0 :                 case WBC_ERR_UNKNOWN_USER:
      51           0 :                         return PAM_USER_UNKNOWN;
      52           0 :                 case WBC_ERR_UNKNOWN_GROUP:
      53           0 :                         return PAM_USER_UNKNOWN;
      54           0 :                 case WBC_ERR_PWD_CHANGE_FAILED:
      55           0 :                         break;
      56             :         }
      57             : 
      58             :         /* be paranoid */
      59           0 :         return PAM_AUTH_ERR;
      60             : }
      61             : 
      62         336 : static const char *_pam_error_code_str(int err)
      63             : {
      64         336 :         switch (err) {
      65         184 :                 case PAM_SUCCESS:
      66         184 :                         return "PAM_SUCCESS";
      67           0 :                 case PAM_OPEN_ERR:
      68           0 :                         return "PAM_OPEN_ERR";
      69           0 :                 case PAM_SYMBOL_ERR:
      70           0 :                         return "PAM_SYMBOL_ERR";
      71           0 :                 case PAM_SERVICE_ERR:
      72           0 :                         return "PAM_SERVICE_ERR";
      73           0 :                 case PAM_SYSTEM_ERR:
      74           0 :                         return "PAM_SYSTEM_ERR";
      75           0 :                 case PAM_BUF_ERR:
      76           0 :                         return "PAM_BUF_ERR";
      77           0 :                 case PAM_PERM_DENIED:
      78           0 :                         return "PAM_PERM_DENIED";
      79         152 :                 case PAM_AUTH_ERR:
      80         152 :                         return "PAM_AUTH_ERR";
      81           0 :                 case PAM_CRED_INSUFFICIENT:
      82           0 :                         return "PAM_CRED_INSUFFICIENT";
      83           0 :                 case PAM_AUTHINFO_UNAVAIL:
      84           0 :                         return "PAM_AUTHINFO_UNAVAIL";
      85           0 :                 case PAM_USER_UNKNOWN:
      86           0 :                         return "PAM_USER_UNKNOWN";
      87           0 :                 case PAM_MAXTRIES:
      88           0 :                         return "PAM_MAXTRIES";
      89           0 :                 case PAM_NEW_AUTHTOK_REQD:
      90           0 :                         return "PAM_NEW_AUTHTOK_REQD";
      91           0 :                 case PAM_ACCT_EXPIRED:
      92           0 :                         return "PAM_ACCT_EXPIRED";
      93           0 :                 case PAM_SESSION_ERR:
      94           0 :                         return "PAM_SESSION_ERR";
      95           0 :                 case PAM_CRED_UNAVAIL:
      96           0 :                         return "PAM_CRED_UNAVAIL";
      97           0 :                 case PAM_CRED_EXPIRED:
      98           0 :                         return "PAM_CRED_EXPIRED";
      99           0 :                 case PAM_CRED_ERR:
     100           0 :                         return "PAM_CRED_ERR";
     101           0 :                 case PAM_NO_MODULE_DATA:
     102           0 :                         return "PAM_NO_MODULE_DATA";
     103           0 :                 case PAM_CONV_ERR:
     104           0 :                         return "PAM_CONV_ERR";
     105           0 :                 case PAM_AUTHTOK_ERR:
     106           0 :                         return "PAM_AUTHTOK_ERR";
     107           0 :                 case PAM_AUTHTOK_RECOVER_ERR:
     108           0 :                         return "PAM_AUTHTOK_RECOVER_ERR";
     109           0 :                 case PAM_AUTHTOK_LOCK_BUSY:
     110           0 :                         return "PAM_AUTHTOK_LOCK_BUSY";
     111           0 :                 case PAM_AUTHTOK_DISABLE_AGING:
     112           0 :                         return "PAM_AUTHTOK_DISABLE_AGING";
     113           0 :                 case PAM_TRY_AGAIN:
     114           0 :                         return "PAM_TRY_AGAIN";
     115           0 :                 case PAM_IGNORE:
     116           0 :                         return "PAM_IGNORE";
     117           0 :                 case PAM_ABORT:
     118           0 :                         return "PAM_ABORT";
     119           0 :                 case PAM_AUTHTOK_EXPIRED:
     120           0 :                         return "PAM_AUTHTOK_EXPIRED";
     121             : #ifdef PAM_MODULE_UNKNOWN
     122           0 :                 case PAM_MODULE_UNKNOWN:
     123           0 :                         return "PAM_MODULE_UNKNOWN";
     124             : #endif
     125             : #ifdef PAM_BAD_ITEM
     126           0 :                 case PAM_BAD_ITEM:
     127           0 :                         return "PAM_BAD_ITEM";
     128             : #endif
     129             : #ifdef PAM_CONV_AGAIN
     130           0 :                 case PAM_CONV_AGAIN:
     131           0 :                         return "PAM_CONV_AGAIN";
     132             : #endif
     133             : #ifdef PAM_INCOMPLETE
     134           0 :                 case PAM_INCOMPLETE:
     135           0 :                         return "PAM_INCOMPLETE";
     136             : #endif
     137           0 :                 default:
     138           0 :                         return NULL;
     139             :         }
     140             : }
     141             : 
     142             : #define _PAM_LOG_FUNCTION_ENTER(function, ctx) \
     143             :         do { \
     144             :                 _pam_log_debug(ctx, LOG_DEBUG, "[pamh: %p] ENTER: " \
     145             :                                function " (flags: 0x%04x)", ctx->pamh, ctx->flags); \
     146             :                 _pam_log_state(ctx); \
     147             :         } while (0)
     148             : 
     149             : #define _PAM_LOG_FUNCTION_LEAVE(function, ctx, retval) \
     150             :         do { \
     151             :                 _pam_log_debug(ctx, LOG_DEBUG, "[pamh: %p] LEAVE: " \
     152             :                                function " returning %d (%s)", ctx ? ctx->pamh : NULL, retval, \
     153             :                                _pam_error_code_str(retval)); \
     154             :                 _pam_log_state(ctx); \
     155             :         } while (0)
     156             : 
     157             : /* data tokens */
     158             : 
     159             : #define MAX_PASSWD_TRIES        3
     160             : 
     161             : #ifdef HAVE_GETTEXT
     162             : static char initialized = 0;
     163             : 
     164             : static inline void textdomain_init(void);
     165         260 : static inline void textdomain_init(void)
     166             : {
     167         260 :         if (!initialized) {
     168         248 :                 bindtextdomain(MODULE_NAME, LOCALEDIR);
     169         248 :                 initialized = 1;
     170             :         }
     171         260 :         return;
     172             : }
     173             : #endif
     174             : 
     175             : 
     176             : /* some syslogging */
     177             : static void _pam_log_int(const pam_handle_t *pamh,
     178             :                          int err,
     179             :                          const char *format,
     180             :                          va_list args) PRINTF_ATTRIBUTE(3, 0);
     181             : 
     182             : #ifdef HAVE_PAM_VSYSLOG
     183        4250 : static void _pam_log_int(const pam_handle_t *pamh,
     184             :                          int err,
     185             :                          const char *format,
     186             :                          va_list args)
     187             : {
     188        4250 :         pam_vsyslog(pamh, err, format, args);
     189        4250 : }
     190             : #else
     191             : static void _pam_log_int(const pam_handle_t *pamh,
     192             :                          int err,
     193             :                          const char *format,
     194             :                          va_list args)
     195             : {
     196             :         char *base = NULL;
     197             :         va_list args2;
     198             :         const char *service;
     199             :         int ret;
     200             : 
     201             :         va_copy(args2, args);
     202             : 
     203             :         pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
     204             : 
     205             :         ret = vasprintf(&base, format, args);
     206             :         if (ret == -1) {
     207             :                 /* what else todo ? */
     208             :                 vsyslog(err, format, args2);
     209             :                 va_end(args2);
     210             :                 return;
     211             :         }
     212             : 
     213             :         syslog(err, "%s(%s): %s", MODULE_NAME, service, base);
     214             :         SAFE_FREE(base);
     215             :         va_end(args2);
     216             : }
     217             : #endif /* HAVE_PAM_VSYSLOG */
     218             : 
     219        4770 : static bool _pam_log_is_silent(int ctrl)
     220             : {
     221        4770 :         return on(ctrl, WINBIND_SILENT);
     222             : }
     223             : 
     224             : static void _pam_log(struct pwb_context *r, int err, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
     225         616 : static void _pam_log(struct pwb_context *r, int err, const char *format, ...)
     226             : {
     227             :         va_list args;
     228             : 
     229         616 :         if (_pam_log_is_silent(r->ctrl)) {
     230           0 :                 return;
     231             :         }
     232             : 
     233         616 :         va_start(args, format);
     234         616 :         _pam_log_int(r->pamh, err, format, args);
     235         616 :         va_end(args);
     236             : }
     237             : static void __pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
     238           0 : static void __pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...)
     239             : {
     240             :         va_list args;
     241             : 
     242           0 :         if (_pam_log_is_silent(ctrl)) {
     243           0 :                 return;
     244             :         }
     245             : 
     246           0 :         va_start(args, format);
     247           0 :         _pam_log_int(pamh, err, format, args);
     248           0 :         va_end(args);
     249             : }
     250             : 
     251        4154 : static bool _pam_log_is_debug_enabled(int ctrl)
     252             : {
     253        4154 :         if (ctrl == -1) {
     254           0 :                 return false;
     255             :         }
     256             : 
     257        4154 :         if (_pam_log_is_silent(ctrl)) {
     258           0 :                 return false;
     259             :         }
     260             : 
     261        4154 :         if (!(ctrl & WINBIND_DEBUG_ARG)) {
     262           0 :                 return false;
     263             :         }
     264             : 
     265        4154 :         return true;
     266             : }
     267             : 
     268         720 : static bool _pam_log_is_debug_state_enabled(int ctrl)
     269             : {
     270         720 :         if (!(ctrl & WINBIND_DEBUG_STATE)) {
     271         200 :                 return false;
     272             :         }
     273             : 
     274         520 :         return _pam_log_is_debug_enabled(ctrl);
     275             : }
     276             : 
     277             : static void _pam_log_debug(struct pwb_context *r, int err, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
     278        3634 : static void _pam_log_debug(struct pwb_context *r, int err, const char *format, ...)
     279             : {
     280             :         va_list args;
     281             : 
     282        3634 :         if (!r || !_pam_log_is_debug_enabled(r->ctrl)) {
     283           0 :                 return;
     284             :         }
     285             : 
     286        3634 :         va_start(args, format);
     287        3634 :         _pam_log_int(r->pamh, err, format, args);
     288        3634 :         va_end(args);
     289             : }
     290             : static void __pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
     291           0 : static void __pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...)
     292             : {
     293             :         va_list args;
     294             : 
     295           0 :         if (!_pam_log_is_debug_enabled(ctrl)) {
     296           0 :                 return;
     297             :         }
     298             : 
     299           0 :         va_start(args, format);
     300           0 :         _pam_log_int(pamh, err, format, args);
     301           0 :         va_end(args);
     302             : }
     303             : 
     304        8840 : static void _pam_log_state_datum(struct pwb_context *ctx,
     305             :                                  int item_type,
     306             :                                  const char *key,
     307             :                                  int is_string)
     308             : {
     309        8840 :         const void *data = NULL;
     310        8840 :         if (item_type != 0) {
     311        5200 :                 pam_get_item(ctx->pamh, item_type, &data);
     312             :         } else {
     313        3640 :                 pam_get_data(ctx->pamh, key, &data);
     314             :         }
     315        8840 :         if (data != NULL) {
     316        2116 :                 const char *type = (item_type != 0) ? "ITEM" : "DATA";
     317        2116 :                 if (is_string != 0) {
     318        1560 :                         _pam_log_debug(ctx, LOG_DEBUG,
     319             :                                        "[pamh: %p] STATE: %s(%s) = \"%s\" (%p)",
     320             :                                        ctx->pamh, type, key, (const char *)data,
     321             :                                        data);
     322             :                 } else {
     323         556 :                         _pam_log_debug(ctx, LOG_DEBUG,
     324             :                                        "[pamh: %p] STATE: %s(%s) = %p",
     325             :                                        ctx->pamh, type, key, data);
     326             :                 }
     327             :         }
     328        8840 : }
     329             : 
     330             : #define _PAM_LOG_STATE_DATA_POINTER(ctx, module_data_name) \
     331             :         _pam_log_state_datum(ctx, 0, module_data_name, 0)
     332             : 
     333             : #define _PAM_LOG_STATE_DATA_STRING(ctx, module_data_name) \
     334             :         _pam_log_state_datum(ctx, 0, module_data_name, 1)
     335             : 
     336             : #define _PAM_LOG_STATE_ITEM_POINTER(ctx, item_type) \
     337             :         _pam_log_state_datum(ctx, item_type, #item_type, 0)
     338             : 
     339             : #define _PAM_LOG_STATE_ITEM_STRING(ctx, item_type) \
     340             :         _pam_log_state_datum(ctx, item_type, #item_type, 1)
     341             : 
     342             : #ifdef DEBUG_PASSWORD
     343             : #define _LOG_PASSWORD_AS_STRING 1
     344             : #else
     345             : #define _LOG_PASSWORD_AS_STRING 0
     346             : #endif
     347             : 
     348             : #define _PAM_LOG_STATE_ITEM_PASSWORD(ctx, item_type) \
     349             :         _pam_log_state_datum(ctx, item_type, #item_type, \
     350             :                              _LOG_PASSWORD_AS_STRING)
     351             : /*
     352             :  * wrapper to preserve old behaviour of iniparser which ignored
     353             :  * key values that had no value assigned like
     354             :  *    key =
     355             :  * for a key like above newer iniparser will return a zero-length
     356             :  * string, previously iniparser would return NULL
     357             :  *
     358             :  * JRA: For compatibility, tiniparser behaves like iniparser.
     359             :  */
     360           0 : static const char *tiniparser_getstring_nonempty(struct tiniparser_dictionary *d,
     361             :                         const char *key,
     362             :                         const char *def)
     363             : {
     364           0 :         const char *ret = tiniparser_getstring(d, key, def);
     365           0 :         if (ret && strlen(ret) == 0) {
     366           0 :                 ret = NULL;
     367             :         }
     368           0 :         return ret;
     369             : }
     370             : 
     371         520 : static void _pam_log_state(struct pwb_context *ctx)
     372             : {
     373         520 :         if (!ctx || !_pam_log_is_debug_state_enabled(ctx->ctrl)) {
     374           0 :                 return;
     375             :         }
     376             : 
     377         520 :         _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_SERVICE);
     378         520 :         _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_USER);
     379         520 :         _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_TTY);
     380         520 :         _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_RHOST);
     381         520 :         _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_RUSER);
     382         520 :         _PAM_LOG_STATE_ITEM_PASSWORD(ctx, PAM_OLDAUTHTOK);
     383         520 :         _PAM_LOG_STATE_ITEM_PASSWORD(ctx, PAM_AUTHTOK);
     384         520 :         _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_USER_PROMPT);
     385         520 :         _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_CONV);
     386             : #ifdef PAM_FAIL_DELAY
     387         520 :         _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_FAIL_DELAY);
     388             : #endif
     389             : #ifdef PAM_REPOSITORY
     390             :         _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_REPOSITORY);
     391             : #endif
     392             : 
     393         520 :         _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_HOMEDIR);
     394         520 :         _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_LOGONSCRIPT);
     395         520 :         _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_LOGONSERVER);
     396         520 :         _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_PROFILEPATH);
     397         520 :         _PAM_LOG_STATE_DATA_STRING(ctx,
     398             :                                    PAM_WINBIND_NEW_AUTHTOK_REQD);
     399             :                                    /* Use atoi to get PAM result code */
     400         520 :         _PAM_LOG_STATE_DATA_STRING(ctx,
     401             :                                    PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH);
     402         520 :         _PAM_LOG_STATE_DATA_POINTER(ctx, PAM_WINBIND_PWD_LAST_SET);
     403             : }
     404             : 
     405         460 : static int _pam_parse(const pam_handle_t *pamh,
     406             :                       int flags,
     407             :                       int argc,
     408             :                       const char **argv,
     409             :                       enum pam_winbind_request_type type,
     410             :                       struct tiniparser_dictionary **result_d)
     411             : {
     412         460 :         int ctrl = 0;
     413         460 :         const char *config_file = NULL;
     414             :         int i;
     415             :         const char **v;
     416         460 :         struct tiniparser_dictionary *d = NULL;
     417             : 
     418         460 :         if (flags & PAM_SILENT) {
     419           0 :                 ctrl |= WINBIND_SILENT;
     420             :         }
     421             : 
     422        1264 :         for (i=argc,v=argv; i-- > 0; ++v) {
     423         804 :                 if (!strncasecmp(*v, "config", strlen("config"))) {
     424           0 :                         ctrl |= WINBIND_CONFIG_FILE;
     425           0 :                         config_file = v[i];
     426           0 :                         break;
     427             :                 }
     428             :         }
     429             : 
     430         460 :         if (config_file == NULL) {
     431         460 :                 config_file = PAM_WINBIND_CONFIG_FILE;
     432             :         }
     433             : 
     434         460 :         d = tiniparser_load(config_file);
     435         460 :         if (d == NULL) {
     436         460 :                 goto config_from_pam;
     437             :         }
     438             : 
     439           0 :         if (tiniparser_getboolean(d, "global:debug", false)) {
     440           0 :                 ctrl |= WINBIND_DEBUG_ARG;
     441             :         }
     442             : 
     443           0 :         if (tiniparser_getboolean(d, "global:debug_state", false)) {
     444           0 :                 ctrl |= WINBIND_DEBUG_STATE;
     445             :         }
     446             : 
     447           0 :         if (tiniparser_getboolean(d, "global:cached_login", false)) {
     448           0 :                 ctrl |= WINBIND_CACHED_LOGIN;
     449             :         }
     450             : 
     451           0 :         if (tiniparser_getboolean(d, "global:krb5_auth", false)) {
     452           0 :                 ctrl |= WINBIND_KRB5_AUTH;
     453             :         }
     454             : 
     455           0 :         if (tiniparser_getboolean(d, "global:silent", false)) {
     456           0 :                 ctrl |= WINBIND_SILENT;
     457             :         }
     458             : 
     459           0 :         if (tiniparser_getstring_nonempty(d, "global:krb5_ccache_type", NULL) != NULL) {
     460           0 :                 ctrl |= WINBIND_KRB5_CCACHE_TYPE;
     461             :         }
     462             : 
     463           0 :         if ((tiniparser_getstring_nonempty(d, "global:require-membership-of", NULL)
     464           0 :              != NULL) ||
     465           0 :             (tiniparser_getstring_nonempty(d, "global:require_membership_of", NULL)
     466             :              != NULL)) {
     467           0 :                 ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
     468             :         }
     469             : 
     470           0 :         if (tiniparser_getboolean(d, "global:try_first_pass", false)) {
     471           0 :                 ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
     472             :         }
     473             : 
     474           0 :         if (tiniparser_getint(d, "global:warn_pwd_expire", 0)) {
     475           0 :                 ctrl |= WINBIND_WARN_PWD_EXPIRE;
     476             :         }
     477             : 
     478           0 :         if (tiniparser_getboolean(d, "global:mkhomedir", false)) {
     479           0 :                 ctrl |= WINBIND_MKHOMEDIR;
     480             :         }
     481             : 
     482           0 :         if (tiniparser_getboolean(d, "global:pwd_change_prompt", false)) {
     483           0 :                 ctrl |= WINBIND_PWD_CHANGE_PROMPT;
     484             :         }
     485             : 
     486         230 : config_from_pam:
     487             :         /* step through arguments */
     488        1264 :         for (i=argc,v=argv; i-- > 0; ++v) {
     489             : 
     490             :                 /* generic options */
     491         804 :                 if (!strcmp(*v,"debug"))
     492         260 :                         ctrl |= WINBIND_DEBUG_ARG;
     493         544 :                 else if (!strcasecmp(*v, "debug_state"))
     494         260 :                         ctrl |= WINBIND_DEBUG_STATE;
     495         284 :                 else if (!strcasecmp(*v, "silent"))
     496           0 :                         ctrl |= WINBIND_SILENT;
     497         284 :                 else if (!strcasecmp(*v, "use_authtok"))
     498           8 :                         ctrl |= WINBIND_USE_AUTHTOK_ARG;
     499         276 :                 else if (!strcasecmp(*v, "try_authtok"))
     500           8 :                         ctrl |= WINBIND_TRY_AUTHTOK_ARG;
     501         268 :                 else if (!strcasecmp(*v, "use_first_pass"))
     502           0 :                         ctrl |= WINBIND_USE_FIRST_PASS_ARG;
     503         268 :                 else if (!strcasecmp(*v, "try_first_pass"))
     504           0 :                         ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
     505         268 :                 else if (!strcasecmp(*v, "unknown_ok"))
     506           0 :                         ctrl |= WINBIND_UNKNOWN_OK_ARG;
     507         268 :                 else if ((type == PAM_WINBIND_AUTHENTICATE
     508          24 :                           || type == PAM_WINBIND_SETCRED)
     509         244 :                          && !strncasecmp(*v, "require_membership_of",
     510             :                                          strlen("require_membership_of")))
     511           0 :                         ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
     512         268 :                 else if ((type == PAM_WINBIND_AUTHENTICATE
     513          24 :                           || type == PAM_WINBIND_SETCRED)
     514         244 :                          && !strncasecmp(*v, "require-membership-of",
     515             :                                          strlen("require-membership-of")))
     516           0 :                         ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
     517         268 :                 else if (!strcasecmp(*v, "krb5_auth"))
     518         130 :                         ctrl |= WINBIND_KRB5_AUTH;
     519         138 :                 else if (!strncasecmp(*v, "krb5_ccache_type",
     520             :                                       strlen("krb5_ccache_type")))
     521         130 :                         ctrl |= WINBIND_KRB5_CCACHE_TYPE;
     522           8 :                 else if (!strcasecmp(*v, "cached_login"))
     523           0 :                         ctrl |= WINBIND_CACHED_LOGIN;
     524           8 :                 else if (!strcasecmp(*v, "mkhomedir"))
     525           0 :                         ctrl |= WINBIND_MKHOMEDIR;
     526           8 :                 else if (!strncasecmp(*v, "warn_pwd_expire",
     527             :                         strlen("warn_pwd_expire")))
     528           8 :                         ctrl |= WINBIND_WARN_PWD_EXPIRE;
     529           0 :                 else if (!strcasecmp(*v, "pwd_change_prompt"))
     530           0 :                         ctrl |= WINBIND_PWD_CHANGE_PROMPT;
     531           0 :                 else if (type != PAM_WINBIND_CLEANUP) {
     532           0 :                         __pam_log(pamh, ctrl, LOG_ERR,
     533             :                                  "pam_parse: unknown option: %s", *v);
     534           0 :                         return -1;
     535             :                 }
     536             : 
     537             :         }
     538             : 
     539         460 :         if (result_d) {
     540         260 :                 *result_d = d;
     541             :         } else {
     542         200 :                 if (d) {
     543           0 :                         tiniparser_freedict(d);
     544             :                 }
     545             :         }
     546             : 
     547         460 :         return ctrl;
     548             : };
     549             : 
     550         260 : static int _pam_winbind_free_context(struct pwb_context *ctx)
     551             : {
     552         260 :         if (!ctx) {
     553           0 :                 return 0;
     554             :         }
     555             : 
     556         260 :         if (ctx->dict) {
     557           0 :                 tiniparser_freedict(ctx->dict);
     558             :         }
     559             : 
     560         260 :         wbcCtxFree(ctx->wbc_ctx);
     561             : 
     562         260 :         return 0;
     563             : }
     564             : 
     565         260 : static int _pam_winbind_init_context(pam_handle_t *pamh,
     566             :                                      int flags,
     567             :                                      int argc,
     568             :                                      const char **argv,
     569             :                                      enum pam_winbind_request_type type,
     570             :                                      struct pwb_context **ctx_p)
     571             : {
     572         260 :         struct pwb_context *r = NULL;
     573         260 :         const char *service = NULL;
     574         260 :         char service_name[32] = {0};
     575             :         int ctrl_code;
     576             : 
     577             : #ifdef HAVE_GETTEXT
     578         260 :         textdomain_init();
     579             : #endif
     580             : 
     581         260 :         r = talloc_zero(NULL, struct pwb_context);
     582         260 :         if (!r) {
     583           0 :                 return PAM_BUF_ERR;
     584             :         }
     585             : 
     586         260 :         talloc_set_destructor(r, _pam_winbind_free_context);
     587             : 
     588         260 :         r->pamh = pamh;
     589         260 :         r->flags = flags;
     590         260 :         r->argc = argc;
     591         260 :         r->argv = argv;
     592         260 :         ctrl_code = _pam_parse(pamh, flags, argc, argv, type, &r->dict);
     593         260 :         if (ctrl_code == -1) {
     594           0 :                 TALLOC_FREE(r);
     595           0 :                 return PAM_SYSTEM_ERR;
     596             :         }
     597         260 :         r->ctrl = ctrl_code;
     598             : 
     599         260 :         r->wbc_ctx = wbcCtxCreate();
     600         260 :         if (r->wbc_ctx == NULL) {
     601           0 :                 TALLOC_FREE(r);
     602           0 :                 return PAM_SYSTEM_ERR;
     603             :         }
     604             : 
     605         260 :         pam_get_item(pamh, PAM_SERVICE, (const void **)&service);
     606             : 
     607         260 :         snprintf(service_name, sizeof(service_name), "PAM_WINBIND[%s]", service);
     608             : 
     609         260 :         wbcSetClientProcessName(service_name);
     610             : 
     611         260 :         *ctx_p = r;
     612             : 
     613         260 :         return PAM_SUCCESS;
     614             : }
     615             : 
     616         200 : static void _pam_winbind_cleanup_func(pam_handle_t *pamh,
     617             :                                       void *data,
     618             :                                       int error_status)
     619             : {
     620         200 :         int ctrl = _pam_parse(pamh, 0, 0, NULL, PAM_WINBIND_CLEANUP, NULL);
     621         200 :         if (_pam_log_is_debug_state_enabled(ctrl)) {
     622           0 :                 __pam_log_debug(pamh, ctrl, LOG_DEBUG,
     623             :                                "[pamh: %p] CLEAN: cleaning up PAM data %p "
     624             :                                "(error_status = %d)", pamh, data,
     625             :                                error_status);
     626             :         }
     627         200 :         TALLOC_FREE(data);
     628         200 : }
     629             : 
     630             : 
     631             : static const struct ntstatus_errors {
     632             :         const char *ntstatus_string;
     633             :         const char *error_string;
     634             : } ntstatus_errors[] = {
     635             :         {"NT_STATUS_OK",
     636             :                 N_("Success")},
     637             :         {"NT_STATUS_BACKUP_CONTROLLER",
     638             :                 N_("No primary Domain Controller available")},
     639             :         {"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
     640             :                 N_("No domain controllers found")},
     641             :         {"NT_STATUS_NO_LOGON_SERVERS",
     642             :                 N_("No logon servers")},
     643             :         {"NT_STATUS_PWD_TOO_SHORT",
     644             :                 N_("Password too short")},
     645             :         {"NT_STATUS_PWD_TOO_RECENT",
     646             :                 N_("The password was recently changed and cannot be changed again before %s")},
     647             :         {"NT_STATUS_PWD_HISTORY_CONFLICT",
     648             :                 N_("Password is already in password history")},
     649             :         {"NT_STATUS_PASSWORD_EXPIRED",
     650             :                 N_("Your password has expired")},
     651             :         {"NT_STATUS_PASSWORD_MUST_CHANGE",
     652             :                 N_("You need to change your password now")},
     653             :         {"NT_STATUS_INVALID_WORKSTATION",
     654             :                 N_("You are not allowed to logon from this workstation")},
     655             :         {"NT_STATUS_INVALID_LOGON_HOURS",
     656             :                 N_("You are not allowed to logon at this time")},
     657             :         {"NT_STATUS_ACCOUNT_EXPIRED",
     658             :                 N_("Your account has expired. "
     659             :                    "Please contact your System administrator")}, /* SCNR */
     660             :         {"NT_STATUS_ACCOUNT_DISABLED",
     661             :                 N_("Your account is disabled. "
     662             :                    "Please contact your System administrator")}, /* SCNR */
     663             :         {"NT_STATUS_ACCOUNT_LOCKED_OUT",
     664             :                 N_("Your account has been locked. "
     665             :                    "Please contact your System administrator")}, /* SCNR */
     666             :         {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT",
     667             :                 N_("Invalid Trust Account")},
     668             :         {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT",
     669             :                 N_("Invalid Trust Account")},
     670             :         {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT",
     671             :                 N_("Invalid Trust Account")},
     672             :         {"NT_STATUS_ACCESS_DENIED",
     673             :                 N_("Access is denied")},
     674             :         {NULL, NULL}
     675             : };
     676             : 
     677          52 : static const char *_get_ntstatus_error_string(const char *nt_status_string)
     678             : {
     679             :         int i;
     680         988 :         for (i=0; ntstatus_errors[i].ntstatus_string != NULL; i++) {
     681         936 :                 if (!strcasecmp(ntstatus_errors[i].ntstatus_string,
     682             :                                 nt_status_string)) {
     683           0 :                         return _(ntstatus_errors[i].error_string);
     684             :                 }
     685             :         }
     686          52 :         return NULL;
     687             : }
     688             : 
     689             : /* --- authentication management functions --- */
     690             : 
     691             : /* Attempt a conversation */
     692             : 
     693         308 : static int converse(const pam_handle_t *pamh,
     694             :                     int nargs,
     695             :                     const struct pam_message **message,
     696             :                     struct pam_response **response)
     697             : {
     698             :         int retval;
     699             :         const struct pam_conv *conv;
     700             : 
     701         308 :         retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
     702         308 :         if (retval == PAM_SUCCESS) {
     703         462 :                 retval = conv->conv(nargs,
     704             :                                     discard_const_p(const struct pam_message *, message),
     705         308 :                                     response, conv->appdata_ptr);
     706             :         }
     707             : 
     708         308 :         return retval; /* propagate error status */
     709             : }
     710             : 
     711             : 
     712          56 : static int _make_remark(struct pwb_context *ctx,
     713             :                         int type,
     714             :                         const char *text)
     715             : {
     716          56 :         int retval = PAM_SUCCESS;
     717             : 
     718             :         const struct pam_message *pmsg[1];
     719             :         struct pam_message msg[1];
     720             :         struct pam_response *resp;
     721             : 
     722          56 :         if (ctx->flags & WINBIND_SILENT) {
     723           0 :                 return PAM_SUCCESS;
     724             :         }
     725             : 
     726          56 :         pmsg[0] = &msg[0];
     727          56 :         msg[0].msg = discard_const_p(char, text);
     728          56 :         msg[0].msg_style = type;
     729             : 
     730          56 :         resp = NULL;
     731          56 :         retval = converse(ctx->pamh, 1, pmsg, &resp);
     732             : 
     733          56 :         if (resp) {
     734        3436 :                 _pam_drop_reply(resp, 1);
     735             :         }
     736          56 :         return retval;
     737             : }
     738             : 
     739             : static int _make_remark_v(struct pwb_context *ctx,
     740             :                           int type,
     741             :                           const char *format,
     742             :                           va_list args) PRINTF_ATTRIBUTE(3, 0);
     743             : 
     744           4 : static int _make_remark_v(struct pwb_context *ctx,
     745             :                           int type,
     746             :                           const char *format,
     747             :                           va_list args)
     748             : {
     749             :         char *var;
     750             :         int ret;
     751             : 
     752           4 :         ret = vasprintf(&var, format, args);
     753           4 :         if (ret < 0) {
     754           0 :                 _pam_log(ctx, LOG_ERR, "memory allocation failure");
     755           0 :                 return ret;
     756             :         }
     757             : 
     758           4 :         ret = _make_remark(ctx, type, var);
     759           4 :         SAFE_FREE(var);
     760           4 :         return ret;
     761             : }
     762             : 
     763             : static int _make_remark_format(struct pwb_context *ctx, int type, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
     764           4 : static int _make_remark_format(struct pwb_context *ctx, int type, const char *format, ...)
     765             : {
     766             :         int ret;
     767             :         va_list args;
     768             : 
     769           4 :         va_start(args, format);
     770           4 :         ret = _make_remark_v(ctx, type, format, args);
     771           4 :         va_end(args);
     772           4 :         return ret;
     773             : }
     774             : 
     775         260 : static int pam_winbind_request_log(struct pwb_context *ctx,
     776             :                                    int retval,
     777             :                                    const char *user,
     778             :                                    const char *fn)
     779             : {
     780         260 :         switch (retval) {
     781          76 :         case PAM_AUTH_ERR:
     782             :                 /* incorrect password */
     783          76 :                 _pam_log(ctx, LOG_WARNING, "user '%s' denied access "
     784             :                          "(incorrect password or invalid membership)", user);
     785          76 :                 return retval;
     786           0 :         case PAM_ACCT_EXPIRED:
     787             :                 /* account expired */
     788           0 :                 _pam_log(ctx, LOG_WARNING, "user '%s' account expired",
     789             :                          user);
     790           0 :                 return retval;
     791           0 :         case PAM_AUTHTOK_EXPIRED:
     792             :                 /* password expired */
     793           0 :                 _pam_log(ctx, LOG_WARNING, "user '%s' password expired",
     794             :                          user);
     795           0 :                 return retval;
     796           0 :         case PAM_NEW_AUTHTOK_REQD:
     797             :                 /* new password required */
     798           0 :                 _pam_log(ctx, LOG_WARNING, "user '%s' new password "
     799             :                          "required", user);
     800           0 :                 return retval;
     801           0 :         case PAM_USER_UNKNOWN:
     802             :                 /* the user does not exist */
     803           0 :                 _pam_log_debug(ctx, LOG_NOTICE, "user '%s' not found",
     804             :                                user);
     805           0 :                 if (ctx->ctrl & WINBIND_UNKNOWN_OK_ARG) {
     806           0 :                         return PAM_IGNORE;
     807             :                 }
     808           0 :                 return retval;
     809           0 :         case PAM_AUTHTOK_ERR:
     810             :                 /* Authentication token manipulation error */
     811           0 :                 _pam_log(ctx, LOG_WARNING, "user `%s' authentication token change failed "
     812             :                         "(pwd complexity/history/min_age not met?)", user);
     813           0 :                 return retval;
     814         184 :         case PAM_SUCCESS:
     815             :                 /* Otherwise, the authentication looked good */
     816         184 :                 if (strcmp(fn, "wbcLogonUser") == 0) {
     817         172 :                         _pam_log(ctx, LOG_NOTICE,
     818             :                                  "user '%s' granted access", user);
     819             :                 } else {
     820          12 :                         _pam_log(ctx, LOG_NOTICE,
     821             :                                  "user '%s' OK", user);
     822             :                 }
     823         184 :                 return retval;
     824           0 :         default:
     825             :                 /* we don't know anything about this return value */
     826           0 :                 _pam_log(ctx, LOG_ERR,
     827             :                          "internal module error (retval = %s(%d), user = '%s')",
     828             :                         _pam_error_code_str(retval), retval, user);
     829           0 :                 return retval;
     830             :         }
     831             : }
     832             : 
     833         260 : static int wbc_auth_error_to_pam_error(struct pwb_context *ctx,
     834             :                                        struct wbcAuthErrorInfo *e,
     835             :                                        wbcErr status,
     836             :                                        const char *username,
     837             :                                        const char *fn)
     838             : {
     839         260 :         int ret = PAM_AUTH_ERR;
     840             : 
     841         260 :         if (WBC_ERROR_IS_OK(status)) {
     842         184 :                 _pam_log_debug(ctx, LOG_DEBUG, "request %s succeeded",
     843             :                         fn);
     844         184 :                 ret = PAM_SUCCESS;
     845         184 :                 return pam_winbind_request_log(ctx, ret, username, fn);
     846             :         }
     847             : 
     848          76 :         if (e) {
     849          76 :                 if (e->pam_error != PAM_SUCCESS) {
     850          76 :                         _pam_log(ctx, LOG_ERR,
     851             :                                  "request %s failed: %s, "
     852             :                                  "PAM error: %s (%d), NTSTATUS: %s, "
     853             :                                  "Error message was: %s",
     854             :                                  fn,
     855             :                                  wbcErrorString(status),
     856             :                                  _pam_error_code_str(e->pam_error),
     857             :                                  e->pam_error,
     858             :                                  e->nt_string,
     859             :                                  e->display_string);
     860          76 :                         ret = e->pam_error;
     861          76 :                         return pam_winbind_request_log(ctx, ret, username, fn);
     862             :                 }
     863             : 
     864           0 :                 _pam_log(ctx, LOG_ERR, "request %s failed, but PAM error 0!", fn);
     865             : 
     866           0 :                 ret = PAM_SERVICE_ERR;
     867           0 :                 return pam_winbind_request_log(ctx, ret, username, fn);
     868             :         }
     869             : 
     870           0 :         ret = wbc_error_to_pam_error(status);
     871           0 :         _pam_log(ctx, LOG_ERR,
     872             :                  "request %s failed: %s, PAM error: %s (%d)!",
     873             :                  fn, wbcErrorString(status),
     874             :                  _pam_error_code_str(ret), ret);
     875           0 :         return pam_winbind_request_log(ctx, ret, username, fn);
     876             : }
     877             : 
     878             : #if defined(HAVE_PAM_RADIO_TYPE)
     879           0 : static bool _pam_winbind_change_pwd(struct pwb_context *ctx)
     880             : {
     881             :         struct pam_message msg;
     882             :         const struct pam_message *pmsg;
     883           0 :         struct pam_response *resp = NULL;
     884             :         int ret;
     885           0 :         bool retval = false;
     886           0 :         pmsg = &msg;
     887           0 :         msg.msg_style = PAM_RADIO_TYPE;
     888           0 :         msg.msg = _("Do you want to change your password now?");
     889           0 :         ret = converse(ctx->pamh, 1, &pmsg, &resp);
     890           0 :         if (resp == NULL) {
     891           0 :                 if (ret == PAM_SUCCESS) {
     892           0 :                         _pam_log(ctx, LOG_CRIT, "pam_winbind: system error!\n");
     893           0 :                         return false;
     894             :                 }
     895             :         }
     896           0 :         if (ret != PAM_SUCCESS) {
     897           0 :                 return false;
     898             :         }
     899           0 :         _pam_log(ctx, LOG_CRIT, "Received [%s] reply from application.\n", resp->resp);
     900             : 
     901           0 :         if ((resp->resp != NULL) && (strcasecmp(resp->resp, "yes") == 0)) {
     902           0 :                 retval = true;
     903             :         }
     904             : 
     905           0 :         _pam_drop_reply(resp, 1);
     906           0 :         return retval;
     907             : }
     908             : #else
     909             : static bool _pam_winbind_change_pwd(struct pwb_context *ctx)
     910             : {
     911             :         return false;
     912             : }
     913             : #endif
     914             : 
     915             : /**
     916             :  * send a password expiry message if required
     917             :  *
     918             :  * @param ctx PAM winbind context.
     919             :  * @param next_change expected (calculated) next expiry date.
     920             :  * @param already_expired pointer to a boolean to indicate if the password is
     921             :  *        already expired.
     922             :  *
     923             :  * @return boolean Returns true if message has been sent, false if not.
     924             :  */
     925             : 
     926         172 : static bool _pam_send_password_expiry_message(struct pwb_context *ctx,
     927             :                                               time_t next_change,
     928             :                                               time_t now,
     929             :                                               int warn_pwd_expire,
     930             :                                               bool *already_expired,
     931             :                                               bool *change_pwd)
     932             : {
     933         172 :         int days = 0;
     934             :         struct tm tm_now, tm_next_change;
     935         172 :         bool retval = false;
     936             :         int ret;
     937             : 
     938         172 :         if (already_expired) {
     939         172 :                 *already_expired = false;
     940             :         }
     941             : 
     942         172 :         if (change_pwd) {
     943         172 :                 *change_pwd = false;
     944             :         }
     945             : 
     946         172 :         if (next_change <= now) {
     947           0 :                 PAM_WB_REMARK_DIRECT(ctx, "NT_STATUS_PASSWORD_EXPIRED");
     948           0 :                 if (already_expired) {
     949           0 :                         *already_expired = true;
     950             :                 }
     951           0 :                 return true;
     952             :         }
     953             : 
     954         258 :         if ((next_change < 0) ||
     955         172 :             (next_change > now + warn_pwd_expire * SECONDS_PER_DAY)) {
     956         168 :                 return false;
     957             :         }
     958             : 
     959           8 :         if ((localtime_r(&now, &tm_now) == NULL) ||
     960           4 :             (localtime_r(&next_change, &tm_next_change) == NULL)) {
     961           0 :                 return false;
     962             :         }
     963             : 
     964           6 :         days = (tm_next_change.tm_yday+tm_next_change.tm_year*365) -
     965           4 :                (tm_now.tm_yday+tm_now.tm_year*365);
     966             : 
     967           4 :         if (days == 0) {
     968           0 :                 ret = _make_remark(ctx, PAM_TEXT_INFO,
     969           0 :                                 _("Your password expires today.\n"));
     970             : 
     971             :                 /*
     972             :                  * If change_pwd and already_expired is null.
     973             :                  * We are just sending a notification message.
     974             :                  * We don't expect any response in this case.
     975             :                  */
     976             : 
     977           0 :                 if (!change_pwd && !already_expired) {
     978           0 :                         return true;
     979             :                 }
     980             : 
     981             :                 /*
     982             :                  * successfully sent the warning message.
     983             :                  * Give the user a chance to change pwd.
     984             :                  */
     985           0 :                 if (ret == PAM_SUCCESS &&
     986           0 :                     (ctx->ctrl & WINBIND_PWD_CHANGE_PROMPT)) {
     987           0 :                         if (change_pwd) {
     988           0 :                                 retval = _pam_winbind_change_pwd(ctx);
     989           0 :                                 if (retval) {
     990           0 :                                         *change_pwd = true;
     991             :                                 }
     992             :                         }
     993             :                 }
     994           0 :                 return true;
     995             :         }
     996             : 
     997           4 :         if (days > 0 && days < warn_pwd_expire) {
     998             : 
     999           6 :                 ret = _make_remark_format(ctx, PAM_TEXT_INFO,
    1000           4 :                                         _("Your password will expire in %d %s.\n"),
    1001           2 :                                         days, (days > 1) ? _("days"):_("day"));
    1002             :                 /*
    1003             :                  * If change_pwd and already_expired is null.
    1004             :                  * We are just sending a notification message.
    1005             :                  * We don't expect any response in this case.
    1006             :                  */
    1007             : 
    1008           4 :                 if (!change_pwd && !already_expired) {
    1009           0 :                         return true;
    1010             :                 }
    1011             : 
    1012             :                 /*
    1013             :                  * successfully sent the warning message.
    1014             :                  * Give the user a chance to change pwd.
    1015             :                  */
    1016           6 :                 if (ret == PAM_SUCCESS &&
    1017           4 :                     (ctx->ctrl & WINBIND_PWD_CHANGE_PROMPT)) {
    1018           0 :                         if (change_pwd) {
    1019           0 :                                 retval = _pam_winbind_change_pwd(ctx);
    1020           0 :                                 if (retval) {
    1021           0 :                                         *change_pwd = true;
    1022             :                                 }
    1023             :                         }
    1024             :                 }
    1025           4 :                 return true;
    1026             :         }
    1027             : 
    1028           0 :         return false;
    1029             : }
    1030             : 
    1031             : /**
    1032             :  * Send a warning if the password expires in the near future
    1033             :  *
    1034             :  * @param ctx PAM winbind context.
    1035             :  * @param response The full authentication response structure.
    1036             :  * @param already_expired boolean, is the pwd already expired?
    1037             :  *
    1038             :  * @return void.
    1039             :  */
    1040             : 
    1041         172 : static void _pam_warn_password_expiry(struct pwb_context *ctx,
    1042             :                                       const struct wbcAuthUserInfo *info,
    1043             :                                       int warn_pwd_expire,
    1044             :                                       bool *already_expired,
    1045             :                                       bool *change_pwd)
    1046             : {
    1047         172 :         time_t now = time(NULL);
    1048         172 :         time_t next_change = 0;
    1049             : 
    1050         172 :         if (info == NULL) {
    1051           0 :                 return;
    1052             :         }
    1053             : 
    1054         172 :         if (already_expired) {
    1055         172 :                 *already_expired = false;
    1056             :         }
    1057             : 
    1058         172 :         if (change_pwd) {
    1059         172 :                 *change_pwd = false;
    1060             :         }
    1061             : 
    1062             :         /* accounts with WBC_ACB_PWNOEXP set never receive a warning */
    1063         172 :         if (info->acct_flags & WBC_ACB_PWNOEXP) {
    1064           0 :                 return;
    1065             :         }
    1066             : 
    1067             :         /* no point in sending a warning if this is a grace logon */
    1068         172 :         if (PAM_WB_GRACE_LOGON(info->user_flags)) {
    1069           0 :                 return;
    1070             :         }
    1071             : 
    1072             :         /* check if the info3 must change timestamp has been set */
    1073         172 :         next_change = info->pass_must_change_time;
    1074             : 
    1075         172 :         if (_pam_send_password_expiry_message(ctx, next_change, now,
    1076             :                                               warn_pwd_expire,
    1077             :                                               already_expired,
    1078             :                                               change_pwd)) {
    1079           4 :                 return;
    1080             :         }
    1081             : 
    1082             :         /* no warning sent */
    1083             : }
    1084             : 
    1085             : #define IS_SID_STRING(name) (strncmp("S-", name, 2) == 0)
    1086             : 
    1087             : /**
    1088             :  * Append a string, making sure not to overflow and to always return a
    1089             :  * NULL-terminated string.
    1090             :  *
    1091             :  * @param dest Destination string buffer (must already be NULL-terminated).
    1092             :  * @param src Source string buffer.
    1093             :  * @param dest_buffer_size Size of dest buffer in bytes.
    1094             :  *
    1095             :  * @return false if dest buffer is not big enough (no bytes copied), true on
    1096             :  * success.
    1097             :  */
    1098             : 
    1099           0 : static bool safe_append_string(char *dest,
    1100             :                                const char *src,
    1101             :                                int dest_buffer_size)
    1102             : {
    1103             :         size_t len;
    1104           0 :         len = strlcat(dest, src, dest_buffer_size);
    1105           0 :         return (len < dest_buffer_size);
    1106             : }
    1107             : 
    1108             : /**
    1109             :  * Convert a names into a SID string, appending it to a buffer.
    1110             :  *
    1111             :  * @param ctx PAM winbind context.
    1112             :  * @param user User in PAM request.
    1113             :  * @param name Name to convert.
    1114             :  * @param sid_list_buffer Where to append the string sid.
    1115             :  * @param sid_list_buffer Size of sid_list_buffer (in bytes).
    1116             :  *
    1117             :  * @return false on failure, true on success.
    1118             :  */
    1119           0 : static bool winbind_name_to_sid_string(struct pwb_context *ctx,
    1120             :                                        const char *user,
    1121             :                                        const char *name,
    1122             :                                        char *sid_list_buffer,
    1123             :                                        int sid_list_buffer_size)
    1124             : {
    1125             :         char sid_string[WBC_SID_STRING_BUFLEN];
    1126             : 
    1127             :         /* lookup name? */
    1128           0 :         if (IS_SID_STRING(name)) {
    1129           0 :                 strlcpy(sid_string, name, sizeof(sid_string));
    1130             :         } else {
    1131             :                 wbcErr wbc_status;
    1132             :                 struct wbcDomainSid sid;
    1133             :                 enum wbcSidType type;
    1134             : 
    1135           0 :                 _pam_log_debug(ctx, LOG_DEBUG,
    1136             :                                "no sid given, looking up: %s\n", name);
    1137             : 
    1138           0 :                 wbc_status = wbcCtxLookupName(ctx->wbc_ctx,
    1139             :                                               "",
    1140             :                                               name,
    1141             :                                               &sid,
    1142             :                                               &type);
    1143           0 :                 if (!WBC_ERROR_IS_OK(wbc_status)) {
    1144           0 :                         _pam_log(ctx, LOG_INFO,
    1145             :                                  "could not lookup name: %s\n", name);
    1146           0 :                         return false;
    1147             :                 }
    1148             : 
    1149           0 :                 wbcSidToStringBuf(&sid, sid_string, sizeof(sid_string));
    1150             :         }
    1151             : 
    1152           0 :         if (!safe_append_string(sid_list_buffer, sid_string,
    1153             :                                 sid_list_buffer_size)) {
    1154           0 :                 return false;
    1155             :         }
    1156           0 :         return true;
    1157             : }
    1158             : 
    1159             : /**
    1160             :  * Convert a list of names into a list of sids.
    1161             :  *
    1162             :  * @param ctx PAM winbind context.
    1163             :  * @param user User in PAM request.
    1164             :  * @param name_list List of names or string sids, separated by commas.
    1165             :  * @param sid_list_buffer Where to put the list of string sids.
    1166             :  * @param sid_list_buffer Size of sid_list_buffer (in bytes).
    1167             :  *
    1168             :  * @return false on failure, true on success.
    1169             :  */
    1170           0 : static bool winbind_name_list_to_sid_string_list(struct pwb_context *ctx,
    1171             :                                                  const char *user,
    1172             :                                                  const char *name_list,
    1173             :                                                  char *sid_list_buffer,
    1174             :                                                  int sid_list_buffer_size)
    1175             : {
    1176           0 :         bool result = false;
    1177           0 :         char *current_name = NULL;
    1178             :         const char *search_location;
    1179             :         const char *comma;
    1180             :         int len;
    1181             : 
    1182           0 :         if (sid_list_buffer_size > 0) {
    1183           0 :                 sid_list_buffer[0] = 0;
    1184             :         }
    1185             : 
    1186           0 :         search_location = name_list;
    1187           0 :         while ((comma = strchr(search_location, ',')) != NULL) {
    1188           0 :                 current_name = strndup(search_location,
    1189           0 :                                        comma - search_location);
    1190           0 :                 if (NULL == current_name) {
    1191           0 :                         goto out;
    1192             :                 }
    1193             : 
    1194           0 :                 if (!winbind_name_to_sid_string(ctx, user,
    1195             :                                                 current_name,
    1196             :                                                 sid_list_buffer,
    1197             :                                                 sid_list_buffer_size)) {
    1198             :                         /*
    1199             :                          * If one group name failed, we must not fail
    1200             :                          * the authentication totally, continue with
    1201             :                          * the following group names. If user belongs to
    1202             :                          * one of the valid groups, we must allow it
    1203             :                          * login. -- BoYang
    1204             :                          */
    1205             : 
    1206           0 :                         _pam_log(ctx, LOG_INFO, "cannot convert group %s to sid, "
    1207             :                                  "check if group %s is valid group.", current_name,
    1208             :                                  current_name);
    1209           0 :                         _make_remark_format(ctx, PAM_TEXT_INFO, _("Cannot convert group %s "
    1210             :                                         "to sid, please contact your administrator to see "
    1211             :                                         "if group %s is valid."), current_name, current_name);
    1212           0 :                         SAFE_FREE(current_name);
    1213           0 :                         search_location = comma + 1;
    1214           0 :                         continue;
    1215             :                 }
    1216             : 
    1217           0 :                 SAFE_FREE(current_name);
    1218             : 
    1219           0 :                 if (!safe_append_string(sid_list_buffer, ",",
    1220             :                                         sid_list_buffer_size)) {
    1221           0 :                         goto out;
    1222             :                 }
    1223             : 
    1224           0 :                 search_location = comma + 1;
    1225             :         }
    1226             : 
    1227           0 :         if (!winbind_name_to_sid_string(ctx, user, search_location,
    1228             :                                         sid_list_buffer,
    1229             :                                         sid_list_buffer_size)) {
    1230           0 :                 _pam_log(ctx, LOG_INFO, "cannot convert group %s to sid, "
    1231             :                          "check if group %s is valid group.", search_location,
    1232             :                          search_location);
    1233           0 :                 _make_remark_format(ctx, PAM_TEXT_INFO, _("Cannot convert group %s "
    1234             :                                 "to sid, please contact your administrator to see "
    1235             :                                 "if group %s is valid."), search_location, search_location);
    1236             : 
    1237             :                 /* If no valid groups were converted we should fail outright */
    1238           0 :                 if (name_list != NULL && strlen(sid_list_buffer) == 0) {
    1239           0 :                         result = false;
    1240           0 :                         goto out;
    1241             :                 }
    1242             :                 /*
    1243             :                  * The lookup of the last name failed..
    1244             :                  * It results in require_member_of_sid ends with ','
    1245             :                  * It is malformatted parameter here, overwrite the last ','.
    1246             :                  */
    1247           0 :                 len = strlen(sid_list_buffer);
    1248           0 :                 if ((len != 0) && (sid_list_buffer[len - 1] == ',')) {
    1249           0 :                         sid_list_buffer[len - 1] = '\0';
    1250             :                 }
    1251             :         }
    1252             : 
    1253           0 :         result = true;
    1254             : 
    1255           0 : out:
    1256           0 :         SAFE_FREE(current_name);
    1257           0 :         return result;
    1258             : }
    1259             : 
    1260             : /**
    1261             :  * put krb5ccname variable into environment
    1262             :  *
    1263             :  * @param ctx PAM winbind context.
    1264             :  * @param krb5ccname env variable retrieved from winbindd.
    1265             :  *
    1266             :  * @return void.
    1267             :  */
    1268             : 
    1269         172 : static void _pam_setup_krb5_env(struct pwb_context *ctx,
    1270             :                                 struct wbcLogonUserInfo *info)
    1271             : {
    1272         172 :         char *var = NULL;
    1273             :         int ret;
    1274             :         uint32_t i;
    1275         172 :         const char *krb5ccname = NULL;
    1276             : 
    1277         172 :         if (off(ctx->ctrl, WINBIND_KRB5_AUTH)) {
    1278         189 :                 return;
    1279             :         }
    1280             : 
    1281          86 :         if (!info) {
    1282           0 :                 return;
    1283             :         }
    1284             : 
    1285         140 :         for (i=0; i < info->num_blobs; i++) {
    1286          80 :                 if (strcasecmp(info->blobs[i].name, "krb5ccname") == 0) {
    1287          26 :                         krb5ccname = (const char *)info->blobs[i].blob.data;
    1288          26 :                         break;
    1289             :                 }
    1290             :         }
    1291             : 
    1292          86 :         if (!krb5ccname || (strlen(krb5ccname) == 0)) {
    1293          60 :                 return;
    1294             :         }
    1295             : 
    1296          26 :         _pam_log_debug(ctx, LOG_DEBUG,
    1297             :                        "request returned KRB5CCNAME: %s", krb5ccname);
    1298             : 
    1299          26 :         if (asprintf(&var, "KRB5CCNAME=%s", krb5ccname) == -1) {
    1300           0 :                 return;
    1301             :         }
    1302             : 
    1303          26 :         ret = pam_putenv(ctx->pamh, var);
    1304          26 :         if (ret != PAM_SUCCESS) {
    1305           0 :                 _pam_log(ctx, LOG_ERR,
    1306             :                          "failed to set KRB5CCNAME to %s: %s",
    1307             :                          var, pam_strerror(ctx->pamh, ret));
    1308             :         }
    1309          26 :         free(var);
    1310             : }
    1311             : 
    1312             : /**
    1313             :  * Copy unix username if available (further processed in PAM).
    1314             :  *
    1315             :  * @param ctx PAM winbind context
    1316             :  * @param user_ret A pointer that holds a pointer to a string
    1317             :  * @param unix_username A username
    1318             :  *
    1319             :  * @return void.
    1320             :  */
    1321             : 
    1322         172 : static void _pam_setup_unix_username(struct pwb_context *ctx,
    1323             :                                      char **user_ret,
    1324             :                                      struct wbcLogonUserInfo *info)
    1325             : {
    1326         172 :         const char *unix_username = NULL;
    1327             :         uint32_t i;
    1328             : 
    1329         172 :         if (!user_ret || !info) {
    1330          12 :                 return;
    1331             :         }
    1332             : 
    1333         186 :         for (i=0; i < info->num_blobs; i++) {
    1334         186 :                 if (strcasecmp(info->blobs[i].name, "unix_username") == 0) {
    1335         160 :                         unix_username = (const char *)info->blobs[i].blob.data;
    1336         160 :                         break;
    1337             :                 }
    1338             :         }
    1339             : 
    1340         160 :         if (!unix_username || !unix_username[0]) {
    1341           0 :                 return;
    1342             :         }
    1343             : 
    1344         160 :         *user_ret = strdup(unix_username);
    1345             : }
    1346             : 
    1347             : /**
    1348             :  * Set string into the PAM stack.
    1349             :  *
    1350             :  * @param ctx PAM winbind context.
    1351             :  * @param data_name Key name for pam_set_data.
    1352             :  * @param value String value.
    1353             :  *
    1354             :  * @return void.
    1355             :  */
    1356             : 
    1357         688 : static void _pam_set_data_string(struct pwb_context *ctx,
    1358             :                                  const char *data_name,
    1359             :                                  const char *value)
    1360             : {
    1361             :         int ret;
    1362             : 
    1363        1032 :         if (!data_name || !value || (strlen(data_name) == 0) ||
    1364         688 :              (strlen(value) == 0)) {
    1365         500 :                 return;
    1366             :         }
    1367             : 
    1368         188 :         ret = pam_set_data(ctx->pamh, data_name, talloc_strdup(NULL, value),
    1369             :                            _pam_winbind_cleanup_func);
    1370         188 :         if (ret != PAM_SUCCESS) {
    1371           0 :                 _pam_log_debug(ctx, LOG_DEBUG,
    1372             :                                "Could not set data %s: %s\n",
    1373             :                                data_name, pam_strerror(ctx->pamh, ret));
    1374             :         }
    1375             : }
    1376             : 
    1377             : /**
    1378             :  * Set info3 strings into the PAM stack.
    1379             :  *
    1380             :  * @param ctx PAM winbind context.
    1381             :  * @param data_name Key name for pam_set_data.
    1382             :  * @param value String value.
    1383             :  *
    1384             :  * @return void.
    1385             :  */
    1386             : 
    1387         172 : static void _pam_set_data_info3(struct pwb_context *ctx,
    1388             :                                 const struct wbcAuthUserInfo *info)
    1389             : {
    1390         172 :         if (info != NULL) {
    1391         172 :                 _pam_set_data_string(ctx, PAM_WINBIND_HOMEDIR,
    1392         172 :                              info->home_directory);
    1393         172 :                 _pam_set_data_string(ctx, PAM_WINBIND_LOGONSCRIPT,
    1394         172 :                              info->logon_script);
    1395         172 :                 _pam_set_data_string(ctx, PAM_WINBIND_LOGONSERVER,
    1396         172 :                              info->logon_server);
    1397         172 :                 _pam_set_data_string(ctx, PAM_WINBIND_PROFILEPATH,
    1398         172 :                              info->profile_path);
    1399             :         }
    1400         172 : }
    1401             : 
    1402             : /**
    1403             :  * Free info3 strings in the PAM stack.
    1404             :  *
    1405             :  * @param pamh PAM handle
    1406             :  *
    1407             :  * @return void.
    1408             :  */
    1409             : 
    1410          76 : static void _pam_free_data_info3(pam_handle_t *pamh)
    1411             : {
    1412          76 :         pam_set_data(pamh, PAM_WINBIND_HOMEDIR, NULL, NULL);
    1413          76 :         pam_set_data(pamh, PAM_WINBIND_LOGONSCRIPT, NULL, NULL);
    1414          76 :         pam_set_data(pamh, PAM_WINBIND_LOGONSERVER, NULL, NULL);
    1415          76 :         pam_set_data(pamh, PAM_WINBIND_PROFILEPATH, NULL, NULL);
    1416          76 : }
    1417             : 
    1418             : /**
    1419             :  * Send PAM_ERROR_MSG for cached or grace logons.
    1420             :  *
    1421             :  * @param ctx PAM winbind context.
    1422             :  * @param username User in PAM request.
    1423             :  * @param info3_user_flgs Info3 flags containing logon type bits.
    1424             :  *
    1425             :  * @return void.
    1426             :  */
    1427             : 
    1428         172 : static void _pam_warn_logon_type(struct pwb_context *ctx,
    1429             :                                  const char *username,
    1430             :                                  uint32_t info3_user_flgs)
    1431             : {
    1432             :         /* inform about logon type */
    1433         172 :         if (PAM_WB_GRACE_LOGON(info3_user_flgs)) {
    1434             : 
    1435           0 :                 _make_remark(ctx, PAM_ERROR_MSG,
    1436           0 :                              _("Grace login. "
    1437             :                                "Please change your password as soon you're "
    1438             :                                "online again"));
    1439           0 :                 _pam_log_debug(ctx, LOG_DEBUG,
    1440             :                                "User %s logged on using grace logon\n",
    1441             :                                username);
    1442             : 
    1443         172 :         } else if (PAM_WB_CACHED_LOGON(info3_user_flgs)) {
    1444             : 
    1445           0 :                 _make_remark(ctx, PAM_ERROR_MSG,
    1446           0 :                              _("Domain Controller unreachable, "
    1447             :                                "using cached credentials instead. "
    1448             :                                "Network resources may be unavailable"));
    1449           0 :                 _pam_log_debug(ctx, LOG_DEBUG,
    1450             :                                "User %s logged on using cached credentials\n",
    1451             :                                username);
    1452             :         }
    1453         172 : }
    1454             : 
    1455             : /**
    1456             :  * Send PAM_ERROR_MSG for krb5 errors.
    1457             :  *
    1458             :  * @param ctx PAM winbind context.
    1459             :  * @param username User in PAM request.
    1460             :  * @param info3_user_flgs Info3 flags containing logon type bits.
    1461             :  *
    1462             :  * @return void.
    1463             :  */
    1464             : 
    1465         172 : static void _pam_warn_krb5_failure(struct pwb_context *ctx,
    1466             :                                    const char *username,
    1467             :                                    uint32_t info3_user_flgs)
    1468             : {
    1469         172 :         if (PAM_WB_KRB5_CLOCK_SKEW(info3_user_flgs)) {
    1470           0 :                 _make_remark(ctx, PAM_ERROR_MSG,
    1471           0 :                              _("Failed to establish your Kerberos Ticket cache "
    1472             :                                "due time differences\n"
    1473             :                                "with the domain controller.  "
    1474             :                                "Please verify the system time.\n"));
    1475           0 :                 _pam_log_debug(ctx, LOG_DEBUG,
    1476             :                                "User %s: Clock skew when getting Krb5 TGT\n",
    1477             :                                username);
    1478             :         }
    1479         172 : }
    1480             : 
    1481        3492 : static bool _pam_check_remark_auth_err(struct pwb_context *ctx,
    1482             :                                        const struct wbcAuthErrorInfo *e,
    1483             :                                        const char *nt_status_string,
    1484             :                                        int *pam_err)
    1485             : {
    1486        3492 :         const char *ntstatus = NULL;
    1487        3492 :         const char *error_string = NULL;
    1488             : 
    1489        3492 :         if (!e || !pam_err) {
    1490        2480 :                 return false;
    1491             :         }
    1492             : 
    1493        1012 :         ntstatus = e->nt_string;
    1494        1012 :         if (!ntstatus) {
    1495           0 :                 return false;
    1496             :         }
    1497             : 
    1498        1012 :         if (strcasecmp(ntstatus, nt_status_string) == 0) {
    1499             : 
    1500          52 :                 error_string = _get_ntstatus_error_string(nt_status_string);
    1501          52 :                 if (error_string) {
    1502           0 :                         _make_remark(ctx, PAM_ERROR_MSG, error_string);
    1503           0 :                         *pam_err = e->pam_error;
    1504           0 :                         return true;
    1505             :                 }
    1506             : 
    1507          52 :                 if (e->display_string) {
    1508          52 :                         _make_remark(ctx, PAM_ERROR_MSG, _(e->display_string));
    1509          52 :                         *pam_err = e->pam_error;
    1510          52 :                         return true;
    1511             :                 }
    1512             : 
    1513           0 :                 _make_remark(ctx, PAM_ERROR_MSG, nt_status_string);
    1514           0 :                 *pam_err = e->pam_error;
    1515             : 
    1516           0 :                 return true;
    1517             :         }
    1518             : 
    1519         960 :         return false;
    1520             : };
    1521             : 
    1522             : /**
    1523             :  * Compose Password Restriction String for a PAM_ERROR_MSG conversation.
    1524             :  *
    1525             :  * @param i The wbcUserPasswordPolicyInfo struct.
    1526             :  *
    1527             :  * @return string (caller needs to talloc_free).
    1528             :  */
    1529             : 
    1530           0 : static char *_pam_compose_pwd_restriction_string(struct pwb_context *ctx,
    1531             :                                                  struct wbcUserPasswordPolicyInfo *i)
    1532             : {
    1533           0 :         char *str = NULL;
    1534             : 
    1535           0 :         if (!i) {
    1536           0 :                 goto failed;
    1537             :         }
    1538             : 
    1539           0 :         str = talloc_asprintf(ctx, _("Your password "));
    1540           0 :         if (!str) {
    1541           0 :                 goto failed;
    1542             :         }
    1543             : 
    1544           0 :         if (i->min_length_password > 0) {
    1545           0 :                 str = talloc_asprintf_append(str,
    1546           0 :                                _("must be at least %d characters; "),
    1547             :                                i->min_length_password);
    1548           0 :                 if (!str) {
    1549           0 :                         goto failed;
    1550             :                 }
    1551             :         }
    1552             : 
    1553           0 :         if (i->password_history > 0) {
    1554           0 :                 str = talloc_asprintf_append(str,
    1555           0 :                                _("cannot repeat any of your previous %d "
    1556             :                                 "passwords; "),
    1557             :                                i->password_history);
    1558           0 :                 if (!str) {
    1559           0 :                         goto failed;
    1560             :                 }
    1561             :         }
    1562             : 
    1563           0 :         if (i->password_properties & WBC_DOMAIN_PASSWORD_COMPLEX) {
    1564           0 :                 str = talloc_asprintf_append(str,
    1565           0 :                                _("must contain capitals, numerals "
    1566             :                                  "or punctuation; "
    1567             :                                  "and cannot contain your account "
    1568             :                                  "or full name; "));
    1569           0 :                 if (!str) {
    1570           0 :                         goto failed;
    1571             :                 }
    1572             :         }
    1573             : 
    1574           0 :         str = talloc_asprintf_append(str,
    1575           0 :                        _("Please type a different password. "
    1576             :                          "Type a password which meets these requirements in "
    1577             :                          "both text boxes."));
    1578           0 :         if (!str) {
    1579           0 :                 goto failed;
    1580             :         }
    1581             : 
    1582           0 :         return str;
    1583             : 
    1584           0 :  failed:
    1585           0 :         TALLOC_FREE(str);
    1586           0 :         return NULL;
    1587             : }
    1588             : 
    1589           0 : static int _pam_create_homedir(struct pwb_context *ctx,
    1590             :                                const char *dirname,
    1591             :                                mode_t mode)
    1592             : {
    1593             :         int ret;
    1594             : 
    1595           0 :         ret = mkdir(dirname, mode);
    1596           0 :         if (ret != 0 && errno == EEXIST) {
    1597             :                 struct stat sbuf;
    1598             : 
    1599           0 :                 ret = stat(dirname, &sbuf);
    1600           0 :                 if (ret != 0) {
    1601           0 :                         return PAM_PERM_DENIED;
    1602             :                 }
    1603             : 
    1604           0 :                 if (!S_ISDIR(sbuf.st_mode)) {
    1605           0 :                         return PAM_PERM_DENIED;
    1606             :                 }
    1607             :         }
    1608             : 
    1609           0 :         if (ret != 0) {
    1610           0 :                 _make_remark_format(ctx, PAM_TEXT_INFO,
    1611           0 :                                     _("Creating directory: %s failed: %s"),
    1612           0 :                                     dirname, strerror(errno));
    1613           0 :                 _pam_log(ctx, LOG_ERR, "could not create dir: %s (%s)",
    1614           0 :                  dirname, strerror(errno));
    1615           0 :                  return PAM_PERM_DENIED;
    1616             :         }
    1617             : 
    1618           0 :         return PAM_SUCCESS;
    1619             : }
    1620             : 
    1621           0 : static int _pam_chown_homedir(struct pwb_context *ctx,
    1622             :                               const char *dirname,
    1623             :                               uid_t uid,
    1624             :                               gid_t gid)
    1625             : {
    1626           0 :         if (chown(dirname, uid, gid) != 0) {
    1627           0 :                 _pam_log(ctx, LOG_ERR, "failed to chown user homedir: %s (%s)",
    1628           0 :                          dirname, strerror(errno));
    1629           0 :                 return PAM_PERM_DENIED;
    1630             :         }
    1631             : 
    1632           0 :         return PAM_SUCCESS;
    1633             : }
    1634             : 
    1635           0 : static int _pam_mkhomedir(struct pwb_context *ctx)
    1636             : {
    1637           0 :         struct passwd *pwd = NULL;
    1638           0 :         char *token = NULL;
    1639           0 :         char *create_dir = NULL;
    1640           0 :         char *user_dir = NULL;
    1641             :         int ret;
    1642             :         const char *username;
    1643           0 :         mode_t mode = 0700;
    1644           0 :         char *safe_ptr = NULL;
    1645           0 :         char *p = NULL;
    1646             : 
    1647             :         /* Get the username */
    1648           0 :         ret = pam_get_user(ctx->pamh, &username, NULL);
    1649           0 :         if ((ret != PAM_SUCCESS) || (!username)) {
    1650           0 :                 _pam_log_debug(ctx, LOG_DEBUG, "can not get the username");
    1651           0 :                 return PAM_SERVICE_ERR;
    1652             :         }
    1653             : 
    1654           0 :         pwd = getpwnam(username);
    1655           0 :         if (pwd == NULL) {
    1656           0 :                 _pam_log_debug(ctx, LOG_DEBUG, "can not get the username");
    1657           0 :                 return PAM_USER_UNKNOWN;
    1658             :         }
    1659           0 :         _pam_log_debug(ctx, LOG_DEBUG, "homedir is: %s", pwd->pw_dir);
    1660             : 
    1661           0 :         ret = _pam_create_homedir(ctx, pwd->pw_dir, 0700);
    1662           0 :         if (ret == PAM_SUCCESS) {
    1663           0 :                 ret = _pam_chown_homedir(ctx, pwd->pw_dir,
    1664             :                                          pwd->pw_uid,
    1665             :                                          pwd->pw_gid);
    1666             :         }
    1667             : 
    1668           0 :         if (ret == PAM_SUCCESS) {
    1669           0 :                 return ret;
    1670             :         }
    1671             : 
    1672             :         /* maybe we need to create parent dirs */
    1673           0 :         create_dir = talloc_strdup(ctx, "/");
    1674           0 :         if (!create_dir) {
    1675           0 :                 return PAM_BUF_ERR;
    1676             :         }
    1677             : 
    1678             :         /* find final directory */
    1679           0 :         user_dir = strrchr(pwd->pw_dir, '/');
    1680           0 :         if (!user_dir) {
    1681           0 :                 return PAM_BUF_ERR;
    1682             :         }
    1683           0 :         user_dir++;
    1684             : 
    1685           0 :         _pam_log(ctx, LOG_DEBUG, "final directory: %s", user_dir);
    1686             : 
    1687           0 :         p = pwd->pw_dir;
    1688             : 
    1689           0 :         while ((token = strtok_r(p, "/", &safe_ptr)) != NULL) {
    1690             : 
    1691           0 :                 mode = 0755;
    1692             : 
    1693           0 :                 p = NULL;
    1694             : 
    1695           0 :                 _pam_log_debug(ctx, LOG_DEBUG, "token is %s", token);
    1696             : 
    1697           0 :                 create_dir = talloc_asprintf_append(create_dir, "%s/", token);
    1698           0 :                 if (!create_dir) {
    1699           0 :                         return PAM_BUF_ERR;
    1700             :                 }
    1701           0 :                 _pam_log_debug(ctx, LOG_DEBUG, "current_dir is %s", create_dir);
    1702             : 
    1703           0 :                 if (strcmp(token, user_dir) == 0) {
    1704           0 :                         _pam_log_debug(ctx, LOG_DEBUG, "assuming last directory: %s", token);
    1705           0 :                         mode = 0700;
    1706             :                 }
    1707             : 
    1708           0 :                 ret = _pam_create_homedir(ctx, create_dir, mode);
    1709           0 :                 if (ret != PAM_SUCCESS) {
    1710           0 :                         return ret;
    1711             :                 }
    1712             :         }
    1713             : 
    1714           0 :         return _pam_chown_homedir(ctx, create_dir,
    1715             :                                   pwd->pw_uid,
    1716             :                                   pwd->pw_gid);
    1717             : }
    1718             : 
    1719             : /* talk to winbindd */
    1720         248 : static int winbind_auth_request(struct pwb_context *ctx,
    1721             :                                 const char *user,
    1722             :                                 const char *pass,
    1723             :                                 const char *member,
    1724             :                                 const char *cctype,
    1725             :                                 const int warn_pwd_expire,
    1726             :                                 struct wbcAuthErrorInfo **p_error,
    1727             :                                 struct wbcLogonUserInfo **p_info,
    1728             :                                 time_t *pwd_last_set,
    1729             :                                 char **user_ret)
    1730             : {
    1731             :         wbcErr wbc_status;
    1732             :         struct wbcLogonUserParams logon;
    1733             :         char membership_of[1024];
    1734         248 :         uid_t user_uid = -1;
    1735         248 :         uint32_t flags = WBFLAG_PAM_INFO3_TEXT;
    1736         248 :         struct wbcLogonUserInfo *info = NULL;
    1737         248 :         struct wbcAuthUserInfo *user_info = NULL;
    1738         248 :         struct wbcAuthErrorInfo *error = NULL;
    1739         248 :         int ret = PAM_AUTH_ERR;
    1740             :         int i;
    1741         248 :         const char *codes[] = {
    1742             :                 "NT_STATUS_PASSWORD_EXPIRED",
    1743             :                 "NT_STATUS_PASSWORD_MUST_CHANGE",
    1744             :                 "NT_STATUS_INVALID_WORKSTATION",
    1745             :                 "NT_STATUS_INVALID_LOGON_HOURS",
    1746             :                 "NT_STATUS_ACCOUNT_EXPIRED",
    1747             :                 "NT_STATUS_ACCOUNT_DISABLED",
    1748             :                 "NT_STATUS_ACCOUNT_LOCKED_OUT",
    1749             :                 "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT",
    1750             :                 "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT",
    1751             :                 "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT",
    1752             :                 "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
    1753             :                 "NT_STATUS_NO_LOGON_SERVERS",
    1754             :                 "NT_STATUS_WRONG_PASSWORD",
    1755             :                 "NT_STATUS_ACCESS_DENIED"
    1756             :         };
    1757             : 
    1758         248 :         if (pwd_last_set) {
    1759          12 :                 *pwd_last_set = 0;
    1760             :         }
    1761             : 
    1762             :         /* Krb5 auth always has to go against the KDC of the user's realm */
    1763             : 
    1764         248 :         if (ctx->ctrl & WINBIND_KRB5_AUTH) {
    1765         124 :                 flags           |= WBFLAG_PAM_CONTACT_TRUSTDOM;
    1766             :         }
    1767             : 
    1768         248 :         if (ctx->ctrl & (WINBIND_KRB5_AUTH|WINBIND_CACHED_LOGIN)) {
    1769         124 :                 struct passwd *pwd = NULL;
    1770             : 
    1771         124 :                 pwd = getpwnam(user);
    1772         124 :                 if (pwd == NULL) {
    1773           0 :                         return PAM_USER_UNKNOWN;
    1774             :                 }
    1775         124 :                 user_uid        = pwd->pw_uid;
    1776             :         }
    1777             : 
    1778         248 :         if (ctx->ctrl & WINBIND_KRB5_AUTH) {
    1779             : 
    1780         124 :                 _pam_log_debug(ctx, LOG_DEBUG,
    1781             :                                "enabling krb5 login flag\n");
    1782             : 
    1783         124 :                 flags           |= WBFLAG_PAM_KRB5 |
    1784             :                                    WBFLAG_PAM_FALLBACK_AFTER_KRB5;
    1785             :         }
    1786             : 
    1787         248 :         if (ctx->ctrl & WINBIND_CACHED_LOGIN) {
    1788           0 :                 _pam_log_debug(ctx, LOG_DEBUG,
    1789             :                                "enabling cached login flag\n");
    1790           0 :                 flags           |= WBFLAG_PAM_CACHED_LOGIN;
    1791             :         }
    1792             : 
    1793         248 :         if (user_ret) {
    1794         236 :                 *user_ret = NULL;
    1795         236 :                 flags           |= WBFLAG_PAM_UNIX_NAME;
    1796             :         }
    1797             : 
    1798         248 :         if (cctype != NULL) {
    1799         118 :                 _pam_log_debug(ctx, LOG_DEBUG,
    1800             :                                "enabling request for a %s krb5 ccache\n",
    1801             :                                cctype);
    1802             :         }
    1803             : 
    1804         248 :         if (member != NULL) {
    1805             : 
    1806           0 :                 ZERO_STRUCT(membership_of);
    1807             : 
    1808           0 :                 if (!winbind_name_list_to_sid_string_list(ctx, user, member,
    1809             :                                                           membership_of,
    1810             :                                                           sizeof(membership_of))) {
    1811           0 :                         _pam_log_debug(ctx, LOG_ERR,
    1812             :                                        "failed to serialize membership of sid "
    1813             :                                        "\"%s\"\n", member);
    1814           0 :                         return PAM_AUTH_ERR;
    1815             :                 }
    1816             :         }
    1817             : 
    1818         248 :         ZERO_STRUCT(logon);
    1819             : 
    1820         248 :         logon.username                  = user;
    1821         248 :         logon.password                  = pass;
    1822             : 
    1823         248 :         if (cctype) {
    1824         118 :                 wbc_status = wbcAddNamedBlob(&logon.num_blobs,
    1825             :                                              &logon.blobs,
    1826             :                                              "krb5_cc_type",
    1827             :                                              0,
    1828             :                                              discard_const_p(uint8_t, cctype),
    1829         118 :                                              strlen(cctype)+1);
    1830         118 :                 if (!WBC_ERROR_IS_OK(wbc_status)) {
    1831           0 :                         goto done;
    1832             :                 }
    1833             :         }
    1834             : 
    1835         248 :         wbc_status = wbcAddNamedBlob(&logon.num_blobs,
    1836             :                                      &logon.blobs,
    1837             :                                      "flags",
    1838             :                                      0,
    1839             :                                      (uint8_t *)&flags,
    1840             :                                      sizeof(flags));
    1841         248 :         if (!WBC_ERROR_IS_OK(wbc_status)) {
    1842           0 :                 goto done;
    1843             :         }
    1844             : 
    1845         248 :         wbc_status = wbcAddNamedBlob(&logon.num_blobs,
    1846             :                                      &logon.blobs,
    1847             :                                      "user_uid",
    1848             :                                      0,
    1849             :                                      (uint8_t *)&user_uid,
    1850             :                                      sizeof(user_uid));
    1851         248 :         if (!WBC_ERROR_IS_OK(wbc_status)) {
    1852           0 :                 goto done;
    1853             :         }
    1854             : 
    1855         248 :         if (member) {
    1856           0 :                 wbc_status = wbcAddNamedBlob(&logon.num_blobs,
    1857             :                                              &logon.blobs,
    1858             :                                              "membership_of",
    1859             :                                              0,
    1860             :                                              (uint8_t *)membership_of,
    1861             :                                              sizeof(membership_of));
    1862           0 :                 if (!WBC_ERROR_IS_OK(wbc_status)) {
    1863           0 :                         goto done;
    1864             :                 }
    1865             :         }
    1866             : 
    1867         248 :         wbc_status = wbcCtxLogonUser(ctx->wbc_ctx,
    1868             :                                      &logon,
    1869             :                                      &info,
    1870             :                                      &error,
    1871             :                                      NULL);
    1872         248 :         ret = wbc_auth_error_to_pam_error(ctx, error, wbc_status,
    1873             :                                           user, "wbcLogonUser");
    1874         248 :         wbcFreeMemory(logon.blobs);
    1875         248 :         logon.blobs = NULL;
    1876             : 
    1877         248 :         if (info && info->info) {
    1878         172 :                 user_info = info->info;
    1879             :         }
    1880             : 
    1881         248 :         if (pwd_last_set && user_info) {
    1882          12 :                 *pwd_last_set = user_info->pass_last_set_time;
    1883             :         }
    1884             : 
    1885         248 :         if (p_info && info) {
    1886           0 :                 *p_info = info;
    1887             :         }
    1888             : 
    1889         248 :         if (p_error && error) {
    1890             :                 /* We want to process the error in the caller. */
    1891           0 :                 *p_error = error;
    1892           0 :                 return ret;
    1893             :         }
    1894             : 
    1895        3616 :         for (i=0; i<ARRAY_SIZE(codes); i++) {
    1896        3420 :                 int _ret = ret;
    1897        3420 :                 if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) {
    1898          52 :                         ret = _ret;
    1899          52 :                         goto done;
    1900             :                 }
    1901             :         }
    1902             : 
    1903         368 :         if ((ret == PAM_SUCCESS) && user_info && info) {
    1904             : 
    1905         172 :                 bool already_expired = false;
    1906         172 :                 bool change_pwd = false;
    1907             : 
    1908             :                 /* warn a user if the password is about to expire soon */
    1909         172 :                 _pam_warn_password_expiry(ctx, user_info,
    1910             :                                           warn_pwd_expire,
    1911             :                                           &already_expired,
    1912             :                                           &change_pwd);
    1913             : 
    1914         172 :                 if (already_expired == true) {
    1915             : 
    1916           0 :                         SMB_TIME_T last_set = user_info->pass_last_set_time;
    1917           0 :                         SMB_TIME_T must_set = user_info->pass_must_change_time;
    1918             : 
    1919           0 :                         _pam_log_debug(ctx, LOG_DEBUG,
    1920             :                                        "Password has expired "
    1921             :                                        "(Password was last set: %lld, "
    1922             :                                        "it must be changed here "
    1923             :                                        "%lld (now it's: %ld))\n",
    1924             :                                        (long long int)last_set,
    1925             :                                        (long long int)must_set,
    1926           0 :                                        (long)time(NULL));
    1927             : 
    1928           0 :                         return PAM_AUTHTOK_EXPIRED;
    1929             :                 }
    1930             : 
    1931         172 :                 if (change_pwd) {
    1932           0 :                         ret = PAM_NEW_AUTHTOK_REQD;
    1933           0 :                         goto done;
    1934             :                 }
    1935             : 
    1936             :                 /* inform about logon type */
    1937         172 :                 _pam_warn_logon_type(ctx, user, user_info->user_flags);
    1938             : 
    1939             :                 /* inform about krb5 failures */
    1940         172 :                 _pam_warn_krb5_failure(ctx, user, user_info->user_flags);
    1941             : 
    1942             :                 /* set some info3 info for other modules in the stack */
    1943         172 :                 _pam_set_data_info3(ctx, user_info);
    1944             : 
    1945             :                 /* put krb5ccname into env */
    1946         172 :                 _pam_setup_krb5_env(ctx, info);
    1947             : 
    1948             :                 /* If winbindd returned a username, return the pointer to it
    1949             :                  * here. */
    1950         172 :                 _pam_setup_unix_username(ctx, user_ret, info);
    1951             :         }
    1952             : 
    1953         148 :  done:
    1954         248 :         wbcFreeMemory(logon.blobs);
    1955         248 :         if (info && info->blobs && !p_info) {
    1956         160 :                 wbcFreeMemory(info->blobs);
    1957             :                 /*
    1958             :                  * We set blobs to NULL to prevent a use after free in the
    1959             :                  * in the wbcLogonUserInfoDestructor
    1960             :                  */
    1961         160 :                 info->blobs = NULL;
    1962             :         }
    1963         248 :         if (error && !p_error) {
    1964          76 :                 wbcFreeMemory(error);
    1965             :         }
    1966         248 :         if (info && !p_info) {
    1967         172 :                 wbcFreeMemory(info);
    1968             :         }
    1969             : 
    1970         248 :         return ret;
    1971             : }
    1972             : 
    1973             : /* talk to winbindd */
    1974          12 : static int winbind_chauthtok_request(struct pwb_context *ctx,
    1975             :                                      const char *user,
    1976             :                                      const char *oldpass,
    1977             :                                      const char *newpass,
    1978             :                                      time_t pwd_last_set)
    1979             : {
    1980             :         wbcErr wbc_status;
    1981             :         struct wbcChangePasswordParams params;
    1982          12 :         struct wbcAuthErrorInfo *error = NULL;
    1983          12 :         struct wbcUserPasswordPolicyInfo *policy = NULL;
    1984          12 :         enum wbcPasswordChangeRejectReason reject_reason = -1;
    1985          12 :         uint32_t flags = 0;
    1986             : 
    1987             :         int i;
    1988          12 :         const char *codes[] = {
    1989             :                 "NT_STATUS_BACKUP_CONTROLLER",
    1990             :                 "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
    1991             :                 "NT_STATUS_NO_LOGON_SERVERS",
    1992             :                 "NT_STATUS_ACCESS_DENIED",
    1993             :                 "NT_STATUS_PWD_TOO_SHORT", /* TODO: tell the min pwd length ? */
    1994             :                 "NT_STATUS_PWD_TOO_RECENT", /* TODO: tell the minage ? */
    1995             :                 "NT_STATUS_PWD_HISTORY_CONFLICT" /* TODO: tell the history length ? */
    1996             :         };
    1997          12 :         int ret = PAM_AUTH_ERR;
    1998             : 
    1999          12 :         ZERO_STRUCT(params);
    2000             : 
    2001          12 :         if (ctx->ctrl & WINBIND_KRB5_AUTH) {
    2002           6 :                 flags |= WBFLAG_PAM_KRB5 |
    2003             :                          WBFLAG_PAM_CONTACT_TRUSTDOM;
    2004             :         }
    2005             : 
    2006          12 :         if (ctx->ctrl & WINBIND_CACHED_LOGIN) {
    2007           0 :                 flags |= WBFLAG_PAM_CACHED_LOGIN;
    2008             :         }
    2009             : 
    2010          12 :         params.account_name             = user;
    2011          12 :         params.level                    = WBC_CHANGE_PASSWORD_LEVEL_PLAIN;
    2012          12 :         params.old_password.plaintext   = oldpass;
    2013          12 :         params.new_password.plaintext   = newpass;
    2014          12 :         params.flags                    = flags;
    2015             : 
    2016          12 :         wbc_status = wbcCtxChangeUserPasswordEx(ctx->wbc_ctx,
    2017             :                                                 &params,
    2018             :                                                 &error,
    2019             :                                                 &reject_reason,
    2020             :                                                 &policy);
    2021          12 :         ret = wbc_auth_error_to_pam_error(ctx, error, wbc_status,
    2022             :                                           user, "wbcChangeUserPasswordEx");
    2023             : 
    2024          12 :         if (WBC_ERROR_IS_OK(wbc_status)) {
    2025          12 :                 _pam_log(ctx, LOG_NOTICE,
    2026             :                          "user '%s' password changed", user);
    2027          12 :                 return PAM_SUCCESS;
    2028             :         }
    2029             : 
    2030           0 :         if (!error) {
    2031           0 :                 wbcFreeMemory(policy);
    2032           0 :                 return ret;
    2033             :         }
    2034             : 
    2035           0 :         for (i=0; i<ARRAY_SIZE(codes); i++) {
    2036           0 :                 int _ret = ret;
    2037           0 :                 if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) {
    2038           0 :                         ret = _ret;
    2039           0 :                         goto done;
    2040             :                 }
    2041             :         }
    2042             : 
    2043           0 :         if (!strcasecmp(error->nt_string,
    2044             :                         "NT_STATUS_PASSWORD_RESTRICTION")) {
    2045             : 
    2046           0 :                 char *pwd_restriction_string = NULL;
    2047           0 :                 SMB_TIME_T min_pwd_age = 0;
    2048             : 
    2049           0 :                 if (policy) {
    2050           0 :                         min_pwd_age     = policy->min_passwordage;
    2051             :                 }
    2052             : 
    2053             :                 /* FIXME: avoid to send multiple PAM messages after another */
    2054           0 :                 switch ((int)reject_reason) {
    2055           0 :                         case -1:
    2056           0 :                                 break;
    2057           0 :                         case WBC_PWD_CHANGE_NO_ERROR:
    2058           0 :                                 if ((min_pwd_age > 0) &&
    2059           0 :                                     (pwd_last_set + min_pwd_age > time(NULL))) {
    2060           0 :                                         time_t next_change = pwd_last_set + min_pwd_age;
    2061             : #if defined(__clang__)
    2062             : #pragma clang diagnostic push
    2063             : #pragma clang diagnostic ignored "-Wformat-nonliteral"
    2064             : #endif
    2065           0 :                                         _make_remark_format(ctx, PAM_ERROR_MSG,
    2066             :                                                 _get_ntstatus_error_string("NT_STATUS_PWD_TOO_RECENT"),
    2067             :                                                 ctime(&next_change));
    2068             : #if defined(__clang__)
    2069             : #pragma clang diagnostic pop
    2070             : #endif
    2071           0 :                                         goto done;
    2072             :                                 }
    2073           0 :                                 break;
    2074           0 :                         case WBC_PWD_CHANGE_PASSWORD_TOO_SHORT:
    2075           0 :                                 PAM_WB_REMARK_DIRECT(ctx,
    2076             :                                         "NT_STATUS_PWD_TOO_SHORT");
    2077           0 :                                 break;
    2078           0 :                         case WBC_PWD_CHANGE_PWD_IN_HISTORY:
    2079           0 :                                 PAM_WB_REMARK_DIRECT(ctx,
    2080             :                                         "NT_STATUS_PWD_HISTORY_CONFLICT");
    2081           0 :                                 break;
    2082           0 :                         case WBC_PWD_CHANGE_NOT_COMPLEX:
    2083           0 :                                 _make_remark(ctx, PAM_ERROR_MSG,
    2084           0 :                                              _("Password does not meet "
    2085             :                                                "complexity requirements"));
    2086           0 :                                 break;
    2087           0 :                         default:
    2088           0 :                                 _pam_log_debug(ctx, LOG_DEBUG,
    2089             :                                                "unknown password change "
    2090             :                                                "reject reason: %d",
    2091             :                                                reject_reason);
    2092           0 :                                 break;
    2093             :                 }
    2094             : 
    2095           0 :                 pwd_restriction_string =
    2096           0 :                         _pam_compose_pwd_restriction_string(ctx, policy);
    2097           0 :                 if (pwd_restriction_string) {
    2098           0 :                         _make_remark(ctx, PAM_ERROR_MSG,
    2099             :                                      pwd_restriction_string);
    2100           0 :                         TALLOC_FREE(pwd_restriction_string);
    2101             :                 }
    2102             :         }
    2103           0 :  done:
    2104           0 :         wbcFreeMemory(error);
    2105           0 :         wbcFreeMemory(policy);
    2106             : 
    2107           0 :         return ret;
    2108             : }
    2109             : 
    2110             : /*
    2111             :  * Checks if a user has an account
    2112             :  *
    2113             :  * return values:
    2114             :  *       1  = User not found
    2115             :  *       0  = OK
    2116             :  *      -1  = System error
    2117             :  */
    2118          24 : static int valid_user(struct pwb_context *ctx,
    2119             :                       const char *user)
    2120             : {
    2121             :         /* check not only if the user is available over NSS calls, also make
    2122             :          * sure it's really a winbind user, this is important when stacking PAM
    2123             :          * modules in the 'account' or 'password' facility. */
    2124             : 
    2125             :         wbcErr wbc_status;
    2126          24 :         struct passwd *pwd = NULL;
    2127          24 :         struct passwd *wb_pwd = NULL;
    2128             : 
    2129          24 :         pwd = getpwnam(user);
    2130          24 :         if (pwd == NULL) {
    2131           0 :                 return 1;
    2132             :         }
    2133             : 
    2134          24 :         wbc_status = wbcCtxGetpwnam(ctx->wbc_ctx, user, &wb_pwd);
    2135          24 :         wbcFreeMemory(wb_pwd);
    2136          24 :         if (!WBC_ERROR_IS_OK(wbc_status)) {
    2137           0 :                 _pam_log(ctx, LOG_DEBUG, "valid_user: wbcGetpwnam gave %s\n",
    2138             :                         wbcErrorString(wbc_status));
    2139             :         }
    2140             : 
    2141          24 :         switch (wbc_status) {
    2142           0 :                 case WBC_ERR_UNKNOWN_USER:
    2143             :                 /* match other insane libwbclient return codes */
    2144             :                 case WBC_ERR_WINBIND_NOT_AVAILABLE:
    2145             :                 case WBC_ERR_DOMAIN_NOT_FOUND:
    2146           0 :                         return 1;
    2147          24 :                 case WBC_ERR_SUCCESS:
    2148          24 :                         return 0;
    2149           0 :                 default:
    2150           0 :                         break;
    2151             :         }
    2152           0 :         return -1;
    2153             : }
    2154             : 
    2155         252 : static char *_pam_delete(register char *xx)
    2156             : {
    2157        1658 :         _pam_overwrite(xx);
    2158         252 :         _pam_drop(xx);
    2159         252 :         return NULL;
    2160             : }
    2161             : 
    2162             : /*
    2163             :  * obtain a password from the user
    2164             :  */
    2165             : 
    2166         260 : static int _winbind_read_password(struct pwb_context *ctx,
    2167             :                                   unsigned int ctrl,
    2168             :                                   const char *comment,
    2169             :                                   const char *prompt1,
    2170             :                                   const char *prompt2,
    2171             :                                   const char **pass)
    2172             : {
    2173             :         int authtok_flag;
    2174             :         int retval;
    2175             :         const char *item;
    2176             :         char *token;
    2177             : 
    2178         260 :         _pam_log(ctx, LOG_DEBUG, "getting password (0x%08x)", ctrl);
    2179             : 
    2180             :         /*
    2181             :          * make sure nothing inappropriate gets returned
    2182             :          */
    2183             : 
    2184         260 :         *pass = token = NULL;
    2185             : 
    2186             :         /*
    2187             :          * which authentication token are we getting?
    2188             :          */
    2189             : 
    2190         260 :         if (on(WINBIND__OLD_PASSWORD, ctrl)) {
    2191          12 :                 authtok_flag = PAM_OLDAUTHTOK;
    2192             :         } else {
    2193         248 :                 authtok_flag = PAM_AUTHTOK;
    2194             :         }
    2195             : 
    2196             :         /*
    2197             :          * should we obtain the password from a PAM item ?
    2198             :          */
    2199             : 
    2200         388 :         if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) ||
    2201         256 :             on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
    2202           8 :                 retval = pam_get_item(ctx->pamh,
    2203             :                                       authtok_flag,
    2204             :                                       (const void **) &item);
    2205           8 :                 if (retval != PAM_SUCCESS) {
    2206             :                         /* very strange. */
    2207           0 :                         _pam_log(ctx, LOG_ALERT,
    2208             :                                  "pam_get_item returned error "
    2209             :                                  "to unix-read-password");
    2210           0 :                         return retval;
    2211           8 :                 } else if (item != NULL) {      /* we have a password! */
    2212           8 :                         *pass = item;
    2213           8 :                         item = NULL;
    2214           8 :                         _pam_log(ctx, LOG_DEBUG,
    2215             :                                  "pam_get_item returned a password");
    2216           8 :                         return PAM_SUCCESS;
    2217           0 :                 } else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
    2218           0 :                         return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
    2219           0 :                 } else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl)
    2220           0 :                            && off(WINBIND__OLD_PASSWORD, ctrl)) {
    2221           0 :                         return PAM_AUTHTOK_RECOVER_ERR;
    2222             :                 }
    2223             :         }
    2224             :         /*
    2225             :          * getting here implies we will have to get the password from the
    2226             :          * user directly.
    2227             :          */
    2228             : 
    2229             :         {
    2230             :                 struct pam_message msg[3];
    2231             :                 const struct pam_message *pmsg[3];
    2232             :                 struct pam_response *resp;
    2233             :                 int i, replies;
    2234             : 
    2235             :                 /* prepare to converse */
    2236             : 
    2237         252 :                 if (comment != NULL && off(ctrl, WINBIND_SILENT)) {
    2238          12 :                         pmsg[0] = &msg[0];
    2239          12 :                         msg[0].msg_style = PAM_TEXT_INFO;
    2240          12 :                         msg[0].msg = discard_const_p(char, comment);
    2241          12 :                         i = 1;
    2242             :                 } else {
    2243         240 :                         i = 0;
    2244             :                 }
    2245             : 
    2246         252 :                 pmsg[i] = &msg[i];
    2247         252 :                 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
    2248         252 :                 msg[i++].msg = discard_const_p(char, prompt1);
    2249         252 :                 replies = 1;
    2250             : 
    2251         252 :                 if (prompt2 != NULL) {
    2252           4 :                         pmsg[i] = &msg[i];
    2253           4 :                         msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
    2254           4 :                         msg[i++].msg = discard_const_p(char, prompt2);
    2255           4 :                         ++replies;
    2256             :                 }
    2257             :                 /* so call the conversation expecting i responses */
    2258         252 :                 resp = NULL;
    2259         252 :                 retval = converse(ctx->pamh, i, pmsg, &resp);
    2260         252 :                 if (resp == NULL) {
    2261           0 :                         if (retval == PAM_SUCCESS) {
    2262           0 :                                 retval = PAM_AUTHTOK_RECOVER_ERR;
    2263             :                         }
    2264           0 :                         goto done;
    2265             :                 }
    2266         252 :                 if (retval != PAM_SUCCESS) {
    2267           0 :                         _pam_drop_reply(resp, i);
    2268           0 :                         goto done;
    2269             :                 }
    2270             : 
    2271             :                 /* interpret the response */
    2272             : 
    2273         252 :                 token = x_strdup(resp[i - replies].resp);
    2274         252 :                 if (!token) {
    2275           0 :                         _pam_log(ctx, LOG_NOTICE,
    2276             :                                  "could not recover "
    2277             :                                  "authentication token");
    2278           0 :                         retval = PAM_AUTHTOK_RECOVER_ERR;
    2279           0 :                         goto done;
    2280             :                 }
    2281             : 
    2282         252 :                 if (replies == 2) {
    2283             :                         /* verify that password entered correctly */
    2284           6 :                         if (!resp[i - 1].resp ||
    2285           4 :                             strcmp(token, resp[i - 1].resp)) {
    2286           0 :                                 _pam_delete(token);     /* mistyped */
    2287           0 :                                 retval = PAM_AUTHTOK_RECOVER_ERR;
    2288           0 :                                 _make_remark(ctx, PAM_ERROR_MSG,
    2289           0 :                                              MISTYPED_PASS);
    2290             :                         }
    2291             :                 }
    2292             : 
    2293             :                 /*
    2294             :                  * tidy up the conversation (resp_retcode) is ignored
    2295             :                  * -- what is it for anyway? AGM
    2296             :                  */
    2297        2110 :                 _pam_drop_reply(resp, i);
    2298             :         }
    2299             : 
    2300         252 :  done:
    2301         252 :         if (retval != PAM_SUCCESS) {
    2302           0 :                 _pam_log_debug(ctx, LOG_DEBUG,
    2303             :                                "unable to obtain a password");
    2304           0 :                 return retval;
    2305             :         }
    2306             :         /* 'token' is the entered password */
    2307             : 
    2308             :         /* we store this password as an item */
    2309             : 
    2310         252 :         retval = pam_set_item(ctx->pamh, authtok_flag, token);
    2311         252 :         _pam_delete(token);     /* clean it up */
    2312         504 :         if (retval != PAM_SUCCESS ||
    2313         252 :             (retval = pam_get_item(ctx->pamh, authtok_flag, (const void **) &item)) != PAM_SUCCESS) {
    2314             : 
    2315           0 :                 _pam_log(ctx, LOG_CRIT, "error manipulating password");
    2316           0 :                 return retval;
    2317             : 
    2318             :         }
    2319             : 
    2320         252 :         *pass = item;
    2321         252 :         item = NULL;            /* break link to password */
    2322             : 
    2323         252 :         return PAM_SUCCESS;
    2324             : }
    2325             : 
    2326         708 : static const char *get_conf_item_string(struct pwb_context *ctx,
    2327             :                                         const char *item,
    2328             :                                         int config_flag)
    2329             : {
    2330         708 :         int i = 0;
    2331         708 :         const char *parm_opt = NULL;
    2332             : 
    2333         708 :         if (!(ctx->ctrl & config_flag)) {
    2334         590 :                 goto out;
    2335             :         }
    2336             : 
    2337             :         /* let the pam opt take precedence over the pam_winbind.conf option */
    2338         476 :         for (i=0; i<ctx->argc; i++) {
    2339             : 
    2340         476 :                 if ((strncmp(ctx->argv[i], item, strlen(item)) == 0)) {
    2341             :                         char *p;
    2342             : 
    2343         118 :                         if ((p = strchr(ctx->argv[i], '=')) == NULL) {
    2344           0 :                                 _pam_log(ctx, LOG_INFO,
    2345             :                                          "no \"=\" delimiter for \"%s\" found\n",
    2346             :                                          item);
    2347           0 :                                 goto out;
    2348             :                         }
    2349         118 :                         _pam_log_debug(ctx, LOG_INFO,
    2350             :                                        "PAM config: %s '%s'\n", item, p+1);
    2351         118 :                         return p + 1;
    2352             :                 }
    2353             :         }
    2354             : 
    2355           0 :         if (ctx->dict) {
    2356           0 :                 char *key = NULL;
    2357             : 
    2358           0 :                 key = talloc_asprintf(ctx, "global:%s", item);
    2359           0 :                 if (!key) {
    2360           0 :                         goto out;
    2361             :                 }
    2362             : 
    2363           0 :                 parm_opt = tiniparser_getstring_nonempty(ctx->dict, key, NULL);
    2364           0 :                 TALLOC_FREE(key);
    2365             : 
    2366           0 :                 _pam_log_debug(ctx, LOG_INFO, "CONFIG file: %s '%s'\n",
    2367             :                                item, parm_opt);
    2368             :         }
    2369         295 : out:
    2370         590 :         return parm_opt;
    2371             : }
    2372             : 
    2373         236 : static int get_config_item_int(struct pwb_context *ctx,
    2374             :                                const char *item,
    2375             :                                int config_flag)
    2376             : {
    2377         236 :         int i, parm_opt = -1;
    2378             : 
    2379         236 :         if (!(ctx->ctrl & config_flag)) {
    2380         228 :                 goto out;
    2381             :         }
    2382             : 
    2383             :         /* let the pam opt take precedence over the pam_winbind.conf option */
    2384          24 :         for (i = 0; i < ctx->argc; i++) {
    2385             : 
    2386          24 :                 if ((strncmp(ctx->argv[i], item, strlen(item)) == 0)) {
    2387             :                         char *p;
    2388             : 
    2389           8 :                         if ((p = strchr(ctx->argv[i], '=')) == NULL) {
    2390           0 :                                 _pam_log(ctx, LOG_INFO,
    2391             :                                          "no \"=\" delimiter for \"%s\" found\n",
    2392             :                                          item);
    2393           0 :                                 goto out;
    2394             :                         }
    2395           8 :                         parm_opt = atoi(p + 1);
    2396           8 :                         _pam_log_debug(ctx, LOG_INFO,
    2397             :                                        "PAM config: %s '%d'\n",
    2398             :                                        item, parm_opt);
    2399           8 :                         return parm_opt;
    2400             :                 }
    2401             :         }
    2402             : 
    2403           0 :         if (ctx->dict) {
    2404           0 :                 char *key = NULL;
    2405             : 
    2406           0 :                 key = talloc_asprintf(ctx, "global:%s", item);
    2407           0 :                 if (!key) {
    2408           0 :                         goto out;
    2409             :                 }
    2410             : 
    2411           0 :                 parm_opt = tiniparser_getint(ctx->dict, key, -1);
    2412           0 :                 TALLOC_FREE(key);
    2413             : 
    2414           0 :                 _pam_log_debug(ctx, LOG_INFO,
    2415             :                                "CONFIG file: %s '%d'\n",
    2416             :                                item, parm_opt);
    2417             :         }
    2418         114 : out:
    2419         228 :         return parm_opt;
    2420             : }
    2421             : 
    2422         236 : static const char *get_krb5_cc_type_from_config(struct pwb_context *ctx)
    2423             : {
    2424         236 :         return get_conf_item_string(ctx, "krb5_ccache_type",
    2425             :                                     WINBIND_KRB5_CCACHE_TYPE);
    2426             : }
    2427             : 
    2428         236 : static const char *get_member_from_config(struct pwb_context *ctx)
    2429             : {
    2430         236 :         const char *ret = NULL;
    2431         236 :         ret = get_conf_item_string(ctx, "require_membership_of",
    2432             :                                    WINBIND_REQUIRED_MEMBERSHIP);
    2433         236 :         if (ret != NULL) {
    2434           0 :                 return ret;
    2435             :         }
    2436         236 :         return get_conf_item_string(ctx, "require-membership-of",
    2437             :                                     WINBIND_REQUIRED_MEMBERSHIP);
    2438             : }
    2439             : 
    2440         236 : static int get_warn_pwd_expire_from_config(struct pwb_context *ctx)
    2441             : {
    2442             :         int ret;
    2443         236 :         ret = get_config_item_int(ctx, "warn_pwd_expire",
    2444             :                                   WINBIND_WARN_PWD_EXPIRE);
    2445             :         /* no or broken setting */
    2446         236 :         if (ret < 0) {
    2447         228 :                 return DEFAULT_DAYS_TO_WARN_BEFORE_PWD_EXPIRES;
    2448             :         }
    2449           8 :         return ret;
    2450             : }
    2451             : 
    2452             : /**
    2453             :  * Retrieve the winbind separator.
    2454             :  *
    2455             :  * @param ctx PAM winbind context.
    2456             :  *
    2457             :  * @return string separator character. NULL on failure.
    2458             :  */
    2459             : 
    2460         144 : static char winbind_get_separator(struct pwb_context *ctx)
    2461             : {
    2462             :         wbcErr wbc_status;
    2463             :         static struct wbcInterfaceDetails *details = NULL;
    2464             : 
    2465         144 :         wbc_status = wbcCtxInterfaceDetails(ctx->wbc_ctx, &details);
    2466         144 :         if (!WBC_ERROR_IS_OK(wbc_status)) {
    2467           0 :                 _pam_log(ctx, LOG_ERR,
    2468             :                          "Could not retrieve winbind interface details: %s",
    2469             :                          wbcErrorString(wbc_status));
    2470           0 :                 return '\0';
    2471             :         }
    2472             : 
    2473         144 :         if (!details) {
    2474           0 :                 return '\0';
    2475             :         }
    2476             : 
    2477         144 :         return details->winbind_separator;
    2478             : }
    2479             : 
    2480             : 
    2481             : /**
    2482             :  * Convert a upn to a name.
    2483             :  *
    2484             :  * @param ctx PAM winbind context.
    2485             :  * @param upn  User UPN to be translated.
    2486             :  *
    2487             :  * @return converted name. NULL pointer on failure. Caller needs to free.
    2488             :  */
    2489             : 
    2490         144 : static char* winbind_upn_to_username(struct pwb_context *ctx,
    2491             :                                      const char *upn)
    2492             : {
    2493             :         char sep;
    2494         144 :         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
    2495             :         struct wbcDomainSid sid;
    2496             :         enum wbcSidType type;
    2497         144 :         char *domain = NULL;
    2498             :         char *name;
    2499             :         char *p;
    2500             :         char *result;
    2501             : 
    2502             :         /* This cannot work when the winbind separator = @ */
    2503             : 
    2504         144 :         sep = winbind_get_separator(ctx);
    2505         144 :         if (!sep || sep == '@') {
    2506           0 :                 return NULL;
    2507             :         }
    2508             : 
    2509         144 :         name = talloc_strdup(ctx, upn);
    2510         144 :         if (!name) {
    2511           0 :                 return NULL;
    2512             :         }
    2513             : 
    2514         144 :         p = strchr(name, '@');
    2515         144 :         if (p == NULL) {
    2516           0 :                 TALLOC_FREE(name);
    2517           0 :                 return NULL;
    2518             :         }
    2519         144 :         *p = '\0';
    2520         144 :         domain = p + 1;
    2521             : 
    2522             :         /* Convert the UPN to a SID */
    2523             : 
    2524         144 :         wbc_status = wbcCtxLookupName(ctx->wbc_ctx, domain, name, &sid, &type);
    2525         144 :         if (!WBC_ERROR_IS_OK(wbc_status)) {
    2526          72 :                 return NULL;
    2527             :         }
    2528             : 
    2529             :         /* Convert the the SID back to the sAMAccountName */
    2530             : 
    2531          72 :         wbc_status = wbcCtxLookupSid(ctx->wbc_ctx, &sid, &domain, &name, &type);
    2532          72 :         if (!WBC_ERROR_IS_OK(wbc_status)) {
    2533           0 :                 return NULL;
    2534             :         }
    2535             : 
    2536          72 :         result = talloc_asprintf(ctx, "%s%c%s", domain, sep, name);
    2537          72 :         wbcFreeMemory(domain);
    2538          72 :         wbcFreeMemory(name);
    2539          72 :         return result;
    2540             : }
    2541             : 
    2542           0 : static int _pam_delete_cred(pam_handle_t *pamh, int flags,
    2543             :                             int argc, enum pam_winbind_request_type type,
    2544             :                             const char **argv)
    2545             : {
    2546           0 :         int retval = PAM_SUCCESS;
    2547           0 :         struct pwb_context *ctx = NULL;
    2548             :         struct wbcLogoffUserParams logoff;
    2549           0 :         struct wbcAuthErrorInfo *error = NULL;
    2550             :         const char *user;
    2551           0 :         wbcErr wbc_status = WBC_ERR_SUCCESS;
    2552             : 
    2553           0 :         ZERO_STRUCT(logoff);
    2554             : 
    2555           0 :         retval = _pam_winbind_init_context(pamh, flags, argc, argv, type, &ctx);
    2556           0 :         if (retval != PAM_SUCCESS) {
    2557           0 :                 return retval;
    2558             :         }
    2559             : 
    2560           0 :         _PAM_LOG_FUNCTION_ENTER("_pam_delete_cred", ctx);
    2561             : 
    2562           0 :         if (ctx->ctrl & WINBIND_KRB5_AUTH) {
    2563             : 
    2564             :                 /* destroy the ccache here */
    2565             : 
    2566           0 :                 uint32_t wbc_flags = 0;
    2567           0 :                 const char *ccname = NULL;
    2568           0 :                 struct passwd *pwd = NULL;
    2569             : 
    2570           0 :                 retval = pam_get_user(pamh, &user, _("Username: "));
    2571           0 :                 if (retval != PAM_SUCCESS) {
    2572           0 :                         _pam_log(ctx, LOG_ERR,
    2573             :                                  "could not identify user");
    2574           0 :                         goto out;
    2575             :                 }
    2576             : 
    2577           0 :                 if (user == NULL) {
    2578           0 :                         _pam_log(ctx, LOG_ERR,
    2579             :                                  "username was NULL!");
    2580           0 :                         retval = PAM_USER_UNKNOWN;
    2581           0 :                         goto out;
    2582             :                 }
    2583             : 
    2584           0 :                 _pam_log_debug(ctx, LOG_DEBUG,
    2585             :                                "username [%s] obtained", user);
    2586             : 
    2587           0 :                 ccname = pam_getenv(pamh, "KRB5CCNAME");
    2588           0 :                 if (ccname == NULL) {
    2589           0 :                         _pam_log_debug(ctx, LOG_DEBUG,
    2590             :                                        "user has no KRB5CCNAME environment");
    2591             :                 }
    2592             : 
    2593           0 :                 pwd = getpwnam(user);
    2594           0 :                 if (pwd == NULL) {
    2595           0 :                         retval = PAM_USER_UNKNOWN;
    2596           0 :                         goto out;
    2597             :                 }
    2598             : 
    2599           0 :                 wbc_flags = WBFLAG_PAM_KRB5 |
    2600             :                         WBFLAG_PAM_CONTACT_TRUSTDOM;
    2601             : 
    2602           0 :                 logoff.username         = user;
    2603             : 
    2604           0 :                 if (ccname) {
    2605           0 :                         wbc_status = wbcAddNamedBlob(&logoff.num_blobs,
    2606             :                                                      &logoff.blobs,
    2607             :                                                      "ccfilename",
    2608             :                                                      0,
    2609             :                                                      discard_const_p(uint8_t, ccname),
    2610           0 :                                                      strlen(ccname)+1);
    2611           0 :                         if (!WBC_ERROR_IS_OK(wbc_status)) {
    2612           0 :                                 goto out;
    2613             :                         }
    2614             :                 }
    2615             : 
    2616           0 :                 wbc_status = wbcAddNamedBlob(&logoff.num_blobs,
    2617             :                                              &logoff.blobs,
    2618             :                                              "flags",
    2619             :                                              0,
    2620             :                                              (uint8_t *)&wbc_flags,
    2621             :                                              sizeof(wbc_flags));
    2622           0 :                 if (!WBC_ERROR_IS_OK(wbc_status)) {
    2623           0 :                         goto out;
    2624             :                 }
    2625             : 
    2626           0 :                 wbc_status = wbcAddNamedBlob(&logoff.num_blobs,
    2627             :                                              &logoff.blobs,
    2628             :                                              "user_uid",
    2629             :                                              0,
    2630           0 :                                              (uint8_t *)&pwd->pw_uid,
    2631             :                                              sizeof(pwd->pw_uid));
    2632           0 :                 if (!WBC_ERROR_IS_OK(wbc_status)) {
    2633           0 :                         goto out;
    2634             :                 }
    2635             : 
    2636           0 :                 wbc_status = wbcCtxLogoffUserEx(ctx->wbc_ctx, &logoff, &error);
    2637           0 :                 retval = wbc_auth_error_to_pam_error(ctx, error, wbc_status,
    2638             :                                                      user, "wbcLogoffUser");
    2639           0 :                 wbcFreeMemory(logoff.blobs);
    2640           0 :                 logoff.blobs = NULL;
    2641             : 
    2642           0 :                 if (!WBC_ERROR_IS_OK(wbc_status)) {
    2643           0 :                         _pam_log(ctx, LOG_INFO,
    2644             :                                  "failed to logoff user %s: %s\n",
    2645             :                                          user, wbcErrorString(wbc_status));
    2646             :                 }
    2647             :         }
    2648             : 
    2649           0 : out:
    2650           0 :         if (logoff.blobs) {
    2651           0 :                 wbcFreeMemory(logoff.blobs);
    2652             :         }
    2653             : 
    2654           0 :         if (!WBC_ERROR_IS_OK(wbc_status)) {
    2655           0 :                 retval = wbc_auth_error_to_pam_error(ctx, error, wbc_status,
    2656             :                      user, "wbcLogoffUser");
    2657             :         }
    2658           0 :         wbcFreeMemory(error);
    2659             : 
    2660             :         /*
    2661             :          * Delete the krb5 ccname variable from the PAM environment
    2662             :          * if it was set by winbind.
    2663             :          */
    2664           0 :         if ((ctx->ctrl & WINBIND_KRB5_AUTH) && pam_getenv(pamh, "KRB5CCNAME")) {
    2665           0 :                 pam_putenv(pamh, "KRB5CCNAME");
    2666             :         }
    2667             : 
    2668           0 :         _PAM_LOG_FUNCTION_LEAVE("_pam_delete_cred", ctx, retval);
    2669             : 
    2670           0 :         TALLOC_FREE(ctx);
    2671             : 
    2672           0 :         return retval;
    2673             : }
    2674             : 
    2675             : #ifdef SECURITY_OPENPAM_H_INCLUDED
    2676             : /*
    2677             :  * Logic below is copied from openpam_check_error_code() in
    2678             :  *./contrib/openpam/lib/libpam/openpam_dispatch.c on FreeBSD.
    2679             :  */
    2680             : static int openpam_convert_error_code(struct pwb_context *ctx,
    2681             :                                       enum pam_winbind_request_type req,
    2682             :                                       int r)
    2683             : {
    2684             :         if (r == PAM_SUCCESS ||
    2685             :             r == PAM_SYSTEM_ERR ||
    2686             :             r == PAM_SERVICE_ERR ||
    2687             :             r == PAM_BUF_ERR ||
    2688             :             r == PAM_CONV_ERR ||
    2689             :             r == PAM_PERM_DENIED ||
    2690             :             r == PAM_ABORT) {
    2691             :                 return r;
    2692             :         }
    2693             : 
    2694             :         /* specific winbind request types */
    2695             :         switch (req) {
    2696             :         case PAM_WINBIND_AUTHENTICATE:
    2697             :                 if (r == PAM_AUTH_ERR ||
    2698             :                     r == PAM_CRED_INSUFFICIENT ||
    2699             :                     r == PAM_AUTHINFO_UNAVAIL ||
    2700             :                     r == PAM_USER_UNKNOWN ||
    2701             :                     r == PAM_MAXTRIES) {
    2702             :                         return r;
    2703             :                 }
    2704             :                 break;
    2705             :         case PAM_WINBIND_SETCRED:
    2706             :                 if (r == PAM_CRED_UNAVAIL ||
    2707             :                     r == PAM_CRED_EXPIRED ||
    2708             :                     r == PAM_USER_UNKNOWN ||
    2709             :                     r == PAM_CRED_ERR) {
    2710             :                         return r;
    2711             :                 }
    2712             :                 break;
    2713             :         case PAM_WINBIND_ACCT_MGMT:
    2714             :                 if (r == PAM_USER_UNKNOWN ||
    2715             :                     r == PAM_AUTH_ERR ||
    2716             :                     r == PAM_NEW_AUTHTOK_REQD ||
    2717             :                     r == PAM_ACCT_EXPIRED) {
    2718             :                         return r;
    2719             :                 }
    2720             :                 break;
    2721             :         case PAM_WINBIND_OPEN_SESSION:
    2722             :         case PAM_WINBIND_CLOSE_SESSION:
    2723             :                 if (r == PAM_SESSION_ERR) {
    2724             :                         return r;
    2725             :                 }
    2726             :                 break;
    2727             :         case PAM_WINBIND_CHAUTHTOK:
    2728             :                 if (r == PAM_PERM_DENIED ||
    2729             :                     r == PAM_AUTHTOK_ERR ||
    2730             :                     r == PAM_AUTHTOK_RECOVERY_ERR ||
    2731             :                     r == PAM_AUTHTOK_LOCK_BUSY ||
    2732             :                     r == PAM_AUTHTOK_DISABLE_AGING ||
    2733             :                     r == PAM_TRY_AGAIN) {
    2734             :                         return r;
    2735             :                 }
    2736             :                 break;
    2737             :         default:
    2738             :                 break;
    2739             :         }
    2740             :         _pam_log(ctx, LOG_INFO,
    2741             :                  "Converting PAM error [%d] to PAM_SERVICE_ERR.\n", r);
    2742             :         return PAM_SERVICE_ERR;
    2743             : };
    2744             : #define pam_error_code(a, b, c) openpam_convert_error_code(a, b, c)
    2745             : #else
    2746             : #define pam_error_code(a, b, c) (c)
    2747             : #endif
    2748             : 
    2749             : _PUBLIC_ PAM_EXTERN
    2750             : int pam_sm_authenticate(pam_handle_t *pamh, int flags,
    2751             :                         int argc, const char **argv)
    2752             : {
    2753             :         const char *username;
    2754             :         const char *password;
    2755         236 :         const char *member = NULL;
    2756         236 :         const char *cctype = NULL;
    2757             :         int warn_pwd_expire;
    2758         236 :         int retval = PAM_AUTH_ERR;
    2759         236 :         char *username_ret = NULL;
    2760         236 :         char *new_authtok_required = NULL;
    2761         236 :         char *real_username = NULL;
    2762         236 :         struct pwb_context *ctx = NULL;
    2763             : 
    2764         236 :         retval = _pam_winbind_init_context(pamh, flags, argc, argv,
    2765             :                                            PAM_WINBIND_AUTHENTICATE, &ctx);
    2766         236 :         if (retval != PAM_SUCCESS) {
    2767           0 :                 return retval;
    2768             :         }
    2769             : 
    2770         236 :         _PAM_LOG_FUNCTION_ENTER("pam_sm_authenticate", ctx);
    2771             : 
    2772             :         /* Get the username */
    2773         236 :         retval = pam_get_user(pamh, &username, NULL);
    2774         236 :         if ((retval != PAM_SUCCESS) || (!username)) {
    2775           0 :                 _pam_log_debug(ctx, LOG_DEBUG,
    2776             :                                "can not get the username");
    2777           0 :                 retval = PAM_SERVICE_ERR;
    2778           0 :                 goto out;
    2779             :         }
    2780             : 
    2781             : 
    2782             : #if defined(AIX)
    2783             :         /* Decode the user name since AIX does not support logn user
    2784             :            names by default.  The name is encoded as _#uid.  */
    2785             : 
    2786             :         if (username[0] == '_') {
    2787             :                 uid_t id = atoi(&username[1]);
    2788             :                 struct passwd *pw = NULL;
    2789             : 
    2790             :                 if ((id!=0) && ((pw = getpwuid(id)) != NULL)) {
    2791             :                         real_username = strdup(pw->pw_name);
    2792             :                 }
    2793             :         }
    2794             : #endif
    2795             : 
    2796         236 :         if (!real_username) {
    2797             :                 /* Just making a copy of the username we got from PAM */
    2798         236 :                 if ((real_username = strdup(username)) == NULL) {
    2799           0 :                         _pam_log_debug(ctx, LOG_DEBUG,
    2800             :                                        "memory allocation failure when copying "
    2801             :                                        "username");
    2802           0 :                         retval = PAM_SERVICE_ERR;
    2803           0 :                         goto out;
    2804             :                 }
    2805             :         }
    2806             : 
    2807             :         /* Maybe this was a UPN */
    2808             : 
    2809         236 :         if (strchr(real_username, '@') != NULL) {
    2810         144 :                 char *samaccountname = NULL;
    2811             : 
    2812         144 :                 samaccountname = winbind_upn_to_username(ctx,
    2813             :                                                          real_username);
    2814         144 :                 if (samaccountname) {
    2815          72 :                         free(real_username);
    2816          72 :                         real_username = strdup(samaccountname);
    2817             :                 }
    2818             :         }
    2819             : 
    2820         236 :         retval = _winbind_read_password(ctx, ctx->ctrl, NULL,
    2821         236 :                                         _("Password: "), NULL,
    2822             :                                         &password);
    2823             : 
    2824         236 :         if (retval != PAM_SUCCESS) {
    2825           0 :                 _pam_log(ctx, LOG_ERR,
    2826             :                          "Could not retrieve user's password");
    2827           0 :                 retval = PAM_AUTHTOK_ERR;
    2828           0 :                 goto out;
    2829             :         }
    2830             : 
    2831             :         /* Let's not give too much away in the log file */
    2832             : 
    2833             : #ifdef DEBUG_PASSWORD
    2834         236 :         _pam_log_debug(ctx, LOG_INFO,
    2835             :                        "Verify user '%s' with password '%s'",
    2836             :                        real_username, password);
    2837             : #else
    2838             :         _pam_log_debug(ctx, LOG_INFO,
    2839             :                        "Verify user '%s'", real_username);
    2840             : #endif
    2841             : 
    2842         236 :         member = get_member_from_config(ctx);
    2843         236 :         cctype = get_krb5_cc_type_from_config(ctx);
    2844         236 :         warn_pwd_expire = get_warn_pwd_expire_from_config(ctx);
    2845             : 
    2846             :         /* Now use the username to look up password */
    2847         236 :         retval = winbind_auth_request(ctx, real_username, password,
    2848             :                                       member, cctype, warn_pwd_expire,
    2849             :                                       NULL, NULL, NULL, &username_ret);
    2850             : 
    2851         236 :         if (retval == PAM_NEW_AUTHTOK_REQD ||
    2852             :             retval == PAM_AUTHTOK_EXPIRED) {
    2853             : 
    2854           0 :                 char *new_authtok_required_during_auth = NULL;
    2855             : 
    2856           0 :                 new_authtok_required = talloc_asprintf(NULL, "%d", retval);
    2857           0 :                 if (!new_authtok_required) {
    2858           0 :                         retval = PAM_BUF_ERR;
    2859           0 :                         goto out;
    2860             :                 }
    2861             : 
    2862           0 :                 pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD,
    2863             :                              new_authtok_required,
    2864             :                              _pam_winbind_cleanup_func);
    2865             : 
    2866           0 :                 retval = PAM_SUCCESS;
    2867             : 
    2868           0 :                 new_authtok_required_during_auth = talloc_asprintf(NULL, "%d", true);
    2869           0 :                 if (!new_authtok_required_during_auth) {
    2870           0 :                         retval = PAM_BUF_ERR;
    2871           0 :                         goto out;
    2872             :                 }
    2873             : 
    2874           0 :                 pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
    2875             :                              new_authtok_required_during_auth,
    2876             :                              _pam_winbind_cleanup_func);
    2877             : 
    2878           0 :                 goto out;
    2879             :         }
    2880             : 
    2881         354 : out:
    2882         236 :         if (username_ret) {
    2883         160 :                 pam_set_item (pamh, PAM_USER, username_ret);
    2884         160 :                 _pam_log_debug(ctx, LOG_INFO,
    2885             :                                "Returned user was '%s'", username_ret);
    2886         160 :                 free(username_ret);
    2887             :         }
    2888             : 
    2889         236 :         if (real_username) {
    2890         236 :                 free(real_username);
    2891             :         }
    2892             : 
    2893         236 :         if (!new_authtok_required) {
    2894         236 :                 pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, NULL, NULL);
    2895             :         }
    2896             : 
    2897         236 :         if (retval != PAM_SUCCESS) {
    2898          76 :                 _pam_free_data_info3(pamh);
    2899             :         }
    2900             : 
    2901         236 :         _PAM_LOG_FUNCTION_LEAVE("pam_sm_authenticate", ctx, retval);
    2902             : 
    2903         236 :         TALLOC_FREE(ctx);
    2904             : 
    2905         236 :         return retval;
    2906             : }
    2907             : 
    2908             : _PUBLIC_ PAM_EXTERN
    2909             : int pam_sm_setcred(pam_handle_t *pamh, int flags,
    2910             :                    int argc, const char **argv)
    2911             : {
    2912           0 :         int ret = PAM_SYSTEM_ERR;
    2913           0 :         struct pwb_context *ctx = NULL;
    2914             : 
    2915           0 :         ret = _pam_winbind_init_context(pamh, flags, argc, argv,
    2916             :                                         PAM_WINBIND_SETCRED, &ctx);
    2917           0 :         if (ret != PAM_SUCCESS) {
    2918           0 :                 return ret;
    2919             :         }
    2920             : 
    2921           0 :         _PAM_LOG_FUNCTION_ENTER("pam_sm_setcred", ctx);
    2922             : 
    2923           0 :         switch (flags & ~PAM_SILENT) {
    2924             : 
    2925           0 :                 case PAM_DELETE_CRED:
    2926           0 :                         ret = _pam_delete_cred(pamh, flags, argc,
    2927             :                                                PAM_WINBIND_SETCRED, argv);
    2928           0 :                         break;
    2929           0 :                 case PAM_REFRESH_CRED:
    2930           0 :                         _pam_log_debug(ctx, LOG_WARNING,
    2931             :                                        "PAM_REFRESH_CRED not implemented");
    2932           0 :                         ret = PAM_SUCCESS;
    2933           0 :                         break;
    2934           0 :                 case PAM_REINITIALIZE_CRED:
    2935           0 :                         _pam_log_debug(ctx, LOG_WARNING,
    2936             :                                        "PAM_REINITIALIZE_CRED not implemented");
    2937           0 :                         ret = PAM_SUCCESS;
    2938           0 :                         break;
    2939           0 :                 case PAM_ESTABLISH_CRED:
    2940           0 :                         _pam_log_debug(ctx, LOG_WARNING,
    2941             :                                        "PAM_ESTABLISH_CRED not implemented");
    2942           0 :                         ret = PAM_SUCCESS;
    2943           0 :                         break;
    2944           0 :                 default:
    2945           0 :                         ret = PAM_SYSTEM_ERR;
    2946           0 :                         break;
    2947             :         }
    2948             : 
    2949           0 :         _PAM_LOG_FUNCTION_LEAVE("pam_sm_setcred", ctx, ret);
    2950             : 
    2951           0 :         TALLOC_FREE(ctx);
    2952             : 
    2953           0 :         return pam_error_code(ctx, PAM_WINBIND_SETCRED, ret);
    2954             : }
    2955             : 
    2956             : /*
    2957             :  * Account management. We want to verify that the account exists
    2958             :  * before returning PAM_SUCCESS
    2959             :  */
    2960             : _PUBLIC_ PAM_EXTERN
    2961             : int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
    2962             :                    int argc, const char **argv)
    2963             : {
    2964             :         const char *username;
    2965           0 :         int ret = PAM_USER_UNKNOWN;
    2966           0 :         const char *tmp = NULL;
    2967           0 :         struct pwb_context *ctx = NULL;
    2968             : 
    2969           0 :         ret = _pam_winbind_init_context(pamh, flags, argc, argv,
    2970             :                                         PAM_WINBIND_ACCT_MGMT, &ctx);
    2971           0 :         if (ret != PAM_SUCCESS) {
    2972           0 :                 return ret;
    2973             :         }
    2974             : 
    2975           0 :         _PAM_LOG_FUNCTION_ENTER("pam_sm_acct_mgmt", ctx);
    2976             : 
    2977             : 
    2978             :         /* Get the username */
    2979           0 :         ret = pam_get_user(pamh, &username, NULL);
    2980           0 :         if ((ret != PAM_SUCCESS) || (!username)) {
    2981           0 :                 _pam_log_debug(ctx, LOG_DEBUG,
    2982             :                                "can not get the username");
    2983           0 :                 ret = PAM_SERVICE_ERR;
    2984           0 :                 goto out;
    2985             :         }
    2986             : 
    2987             :         /* Verify the username */
    2988           0 :         ret = valid_user(ctx, username);
    2989           0 :         switch (ret) {
    2990           0 :         case -1:
    2991             :                 /* some sort of system error. The log was already printed */
    2992           0 :                 ret = PAM_SERVICE_ERR;
    2993           0 :                 goto out;
    2994           0 :         case 1:
    2995             :                 /* the user does not exist */
    2996           0 :                 _pam_log_debug(ctx, LOG_NOTICE, "user '%s' not found",
    2997             :                                username);
    2998           0 :                 if (ctx->ctrl & WINBIND_UNKNOWN_OK_ARG) {
    2999           0 :                         ret = PAM_IGNORE;
    3000           0 :                         goto out;
    3001             :                 }
    3002           0 :                 ret = PAM_USER_UNKNOWN;
    3003           0 :                 goto out;
    3004           0 :         case 0:
    3005           0 :                 pam_get_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD,
    3006             :                              (const void **)&tmp);
    3007           0 :                 if (tmp != NULL) {
    3008           0 :                         ret = atoi(tmp);
    3009           0 :                         switch (ret) {
    3010           0 :                         case PAM_AUTHTOK_EXPIRED:
    3011             :                                 /* Since new token is required in this case */
    3012             :                                 FALL_THROUGH;
    3013             :                         case PAM_NEW_AUTHTOK_REQD:
    3014           0 :                                 _pam_log(ctx, LOG_WARNING,
    3015             :                                          "pam_sm_acct_mgmt success but %s is set",
    3016             :                                          PAM_WINBIND_NEW_AUTHTOK_REQD);
    3017           0 :                                 _pam_log(ctx, LOG_NOTICE,
    3018             :                                          "user '%s' needs new password",
    3019             :                                          username);
    3020             :                                 /* PAM_AUTHTOKEN_REQD does not exist, but is documented in the manpage */
    3021           0 :                                 ret = PAM_NEW_AUTHTOK_REQD;
    3022           0 :                                 goto out;
    3023           0 :                         default:
    3024           0 :                                 _pam_log(ctx, LOG_WARNING,
    3025             :                                          "pam_sm_acct_mgmt success");
    3026           0 :                                 _pam_log(ctx, LOG_NOTICE,
    3027             :                                          "user '%s' granted access", username);
    3028           0 :                                 ret = PAM_SUCCESS;
    3029           0 :                                 goto out;
    3030             :                         }
    3031             :                 }
    3032             : 
    3033             :                 /* Otherwise, the authentication looked good */
    3034           0 :                 _pam_log(ctx, LOG_NOTICE,
    3035             :                          "user '%s' granted access", username);
    3036           0 :                 ret = PAM_SUCCESS;
    3037           0 :                 goto out;
    3038           0 :         default:
    3039             :                 /* we don't know anything about this return value */
    3040           0 :                 _pam_log(ctx, LOG_ERR,
    3041             :                          "internal module error (ret = %d, user = '%s')",
    3042             :                          ret, username);
    3043           0 :                 ret = PAM_SERVICE_ERR;
    3044           0 :                 goto out;
    3045             :         }
    3046             : 
    3047             :         /* should not be reached */
    3048             :         ret = PAM_IGNORE;
    3049             : 
    3050           0 :  out:
    3051             : 
    3052           0 :         _PAM_LOG_FUNCTION_LEAVE("pam_sm_acct_mgmt", ctx, ret);
    3053             : 
    3054           0 :         TALLOC_FREE(ctx);
    3055             : 
    3056           0 :         return pam_error_code(ctx, PAM_WINBIND_ACCT_MGMT, ret);
    3057             : }
    3058             : 
    3059             : _PUBLIC_ PAM_EXTERN
    3060             : int pam_sm_open_session(pam_handle_t *pamh, int flags,
    3061             :                         int argc, const char **argv)
    3062             : {
    3063           0 :         int ret = PAM_SUCCESS;
    3064           0 :         struct pwb_context *ctx = NULL;
    3065             : 
    3066           0 :         ret = _pam_winbind_init_context(pamh, flags, argc, argv,
    3067             :                                         PAM_WINBIND_OPEN_SESSION, &ctx);
    3068           0 :         if (ret != PAM_SUCCESS) {
    3069           0 :                 return ret;
    3070             :         }
    3071             : 
    3072           0 :         _PAM_LOG_FUNCTION_ENTER("pam_sm_open_session", ctx);
    3073             : 
    3074           0 :         if (ctx->ctrl & WINBIND_MKHOMEDIR) {
    3075             :                 /* check and create homedir */
    3076           0 :                 ret = _pam_mkhomedir(ctx);
    3077             :         }
    3078             : 
    3079           0 :         _PAM_LOG_FUNCTION_LEAVE("pam_sm_open_session", ctx, ret);
    3080             : 
    3081           0 :         TALLOC_FREE(ctx);
    3082             : 
    3083           0 :         return pam_error_code(ctx, PAM_WINBIND_OPEN_SESSION, ret);
    3084             : }
    3085             : 
    3086             : _PUBLIC_ PAM_EXTERN
    3087             : int pam_sm_close_session(pam_handle_t *pamh, int flags,
    3088             :                          int argc, const char **argv)
    3089             : {
    3090           0 :         int ret = PAM_SUCCESS;
    3091           0 :         struct pwb_context *ctx = NULL;
    3092             : 
    3093           0 :         ret = _pam_winbind_init_context(pamh, flags, argc, argv,
    3094             :                                         PAM_WINBIND_CLOSE_SESSION, &ctx);
    3095           0 :         if (ret != PAM_SUCCESS) {
    3096           0 :                 return ret;
    3097             :         }
    3098             : 
    3099           0 :         _PAM_LOG_FUNCTION_ENTER("pam_sm_close_session", ctx);
    3100             : 
    3101           0 :         _PAM_LOG_FUNCTION_LEAVE("pam_sm_close_session", ctx, ret);
    3102             : 
    3103           0 :         TALLOC_FREE(ctx);
    3104             : 
    3105           0 :         return pam_error_code(ctx, PAM_WINBIND_CLOSE_SESSION, ret);
    3106             : }
    3107             : 
    3108             : /**
    3109             :  * evaluate whether we need to re-authenticate with kerberos after a
    3110             :  * password change
    3111             :  *
    3112             :  * @param ctx PAM winbind context.
    3113             :  * @param user The username
    3114             :  *
    3115             :  * @return boolean Returns true if required, false if not.
    3116             :  */
    3117             : 
    3118          12 : static bool _pam_require_krb5_auth_after_chauthtok(struct pwb_context *ctx,
    3119             :                                                    const char *user)
    3120             : {
    3121             : 
    3122             :         /* Make sure that we only do this if a) the chauthtok got initiated
    3123             :          * during a logon attempt (authenticate->acct_mgmt->chauthtok) b) any
    3124             :          * later password change via the "passwd" command if done by the user
    3125             :          * itself
    3126             :          * NB. If we login from gdm or xdm and the password expires,
    3127             :          * we change the password, but there is no memory cache.
    3128             :          * Thus, even for passthrough login, we should do the
    3129             :          * authentication again to update memory cache.
    3130             :          * --- BoYang
    3131             :          * */
    3132             : 
    3133          12 :         const char *new_authtok_reqd_during_auth = NULL;
    3134          12 :         struct passwd *pwd = NULL;
    3135             : 
    3136          12 :         pam_get_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
    3137             :                       (const void **) &new_authtok_reqd_during_auth);
    3138          12 :         pam_set_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
    3139             :                      NULL, NULL);
    3140             : 
    3141          12 :         if (new_authtok_reqd_during_auth) {
    3142           0 :                 return true;
    3143             :         }
    3144             : 
    3145          12 :         pwd = getpwnam(user);
    3146          12 :         if (!pwd) {
    3147           0 :                 return false;
    3148             :         }
    3149             : 
    3150          12 :         if (getuid() == pwd->pw_uid) {
    3151           0 :                 return true;
    3152             :         }
    3153             : 
    3154          12 :         return false;
    3155             : }
    3156             : 
    3157             : 
    3158             : _PUBLIC_ PAM_EXTERN
    3159             : int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
    3160             :                      int argc, const char **argv)
    3161             : {
    3162             :         unsigned int lctrl;
    3163             :         int ret;
    3164          24 :         bool cached_login = false;
    3165             : 
    3166             :         /* <DO NOT free() THESE> */
    3167             :         const char *user;
    3168             :         const char *pass_old;
    3169             :         const char *pass_new;
    3170             :         /* </DO NOT free() THESE> */
    3171             : 
    3172             :         char *Announce;
    3173             : 
    3174          24 :         int retry = 0;
    3175          24 :         char *username_ret = NULL;
    3176          24 :         struct wbcAuthErrorInfo *error = NULL;
    3177          24 :         struct pwb_context *ctx = NULL;
    3178             : 
    3179          24 :         ret = _pam_winbind_init_context(pamh, flags, argc, argv,
    3180             :                                         PAM_WINBIND_CHAUTHTOK, &ctx);
    3181          24 :         if (ret != PAM_SUCCESS) {
    3182           0 :                 return ret;
    3183             :         }
    3184             : 
    3185          24 :         _PAM_LOG_FUNCTION_ENTER("pam_sm_chauthtok", ctx);
    3186             : 
    3187          24 :         cached_login = (ctx->ctrl & WINBIND_CACHED_LOGIN);
    3188             : 
    3189             :         /* clearing offline bit for auth */
    3190          24 :         ctx->ctrl &= ~WINBIND_CACHED_LOGIN;
    3191             : 
    3192             :         /*
    3193             :          * First get the name of a user
    3194             :          */
    3195          24 :         ret = pam_get_user(pamh, &user, _("Username: "));
    3196          24 :         if (ret != PAM_SUCCESS) {
    3197           0 :                 _pam_log(ctx, LOG_ERR,
    3198             :                          "password - could not identify user");
    3199           0 :                 goto out;
    3200             :         }
    3201             : 
    3202          24 :         if (user == NULL) {
    3203           0 :                 _pam_log(ctx, LOG_ERR, "username was NULL!");
    3204           0 :                 ret = PAM_USER_UNKNOWN;
    3205           0 :                 goto out;
    3206             :         }
    3207             : 
    3208          24 :         _pam_log_debug(ctx, LOG_DEBUG, "username [%s] obtained", user);
    3209             : 
    3210             :         /* check if this is really a user in winbindd, not only in NSS */
    3211          24 :         ret = valid_user(ctx, user);
    3212          24 :         switch (ret) {
    3213           0 :                 case 1:
    3214           0 :                         ret = PAM_USER_UNKNOWN;
    3215           0 :                         goto out;
    3216           0 :                 case -1:
    3217           0 :                         ret = PAM_SYSTEM_ERR;
    3218           0 :                         goto out;
    3219          24 :                 default:
    3220          24 :                         break;
    3221             :         }
    3222             : 
    3223             :         /*
    3224             :          * obtain and verify the current password (OLDAUTHTOK) for
    3225             :          * the user.
    3226             :          */
    3227             : 
    3228          24 :         if (flags & PAM_PRELIM_CHECK) {
    3229          12 :                 time_t *pwdlastset_prelim = NULL;
    3230             : 
    3231          12 :                 pwdlastset_prelim = talloc_zero(NULL, time_t);
    3232          12 :                 if (pwdlastset_prelim == NULL) {
    3233           0 :                         _pam_log(ctx, LOG_CRIT,
    3234             :                                  "password - out of memory");
    3235           0 :                         ret = PAM_BUF_ERR;
    3236           0 :                         goto out;
    3237             :                 }
    3238             : 
    3239             :                 /* instruct user what is happening */
    3240             : 
    3241             : #define greeting _("Changing password for")
    3242          12 :                 Announce = talloc_asprintf(ctx, "%s %s", greeting, user);
    3243          12 :                 if (!Announce) {
    3244           0 :                         _pam_log(ctx, LOG_CRIT,
    3245             :                                  "password - out of memory");
    3246           0 :                         ret = PAM_BUF_ERR;
    3247           0 :                         goto out;
    3248             :                 }
    3249             : #undef greeting
    3250             : 
    3251          12 :                 lctrl = ctx->ctrl | WINBIND__OLD_PASSWORD;
    3252          12 :                 ret = _winbind_read_password(ctx, lctrl,
    3253             :                                                 Announce,
    3254          12 :                                                 _("(current) NT password: "),
    3255             :                                                 NULL,
    3256             :                                                 (const char **) &pass_old);
    3257          12 :                 TALLOC_FREE(Announce);
    3258          12 :                 if (ret != PAM_SUCCESS) {
    3259           0 :                         _pam_log(ctx, LOG_NOTICE,
    3260             :                                  "password - (old) token not obtained");
    3261           0 :                         goto out;
    3262             :                 }
    3263             : 
    3264             :                 /* verify that this is the password for this user */
    3265             : 
    3266          12 :                 ret = winbind_auth_request(ctx, user, pass_old,
    3267             :                                            NULL, NULL, 0,
    3268             :                                            &error, NULL,
    3269             :                                            pwdlastset_prelim, NULL);
    3270             : 
    3271          12 :                 if (ret != PAM_ACCT_EXPIRED &&
    3272          12 :                     ret != PAM_AUTHTOK_EXPIRED &&
    3273          12 :                     ret != PAM_NEW_AUTHTOK_REQD &&
    3274             :                     ret != PAM_SUCCESS) {
    3275           0 :                         pass_old = NULL;
    3276           0 :                         goto out;
    3277             :                 }
    3278             : 
    3279          12 :                 pam_set_data(pamh, PAM_WINBIND_PWD_LAST_SET,
    3280             :                              pwdlastset_prelim,
    3281             :                              _pam_winbind_cleanup_func);
    3282             : 
    3283          12 :                 ret = pam_set_item(pamh, PAM_OLDAUTHTOK,
    3284             :                                    (const void *) pass_old);
    3285          12 :                 pass_old = NULL;
    3286          12 :                 if (ret != PAM_SUCCESS) {
    3287           0 :                         _pam_log(ctx, LOG_CRIT,
    3288             :                                  "failed to set PAM_OLDAUTHTOK");
    3289             :                 }
    3290          12 :         } else if (flags & PAM_UPDATE_AUTHTOK) {
    3291             : 
    3292          12 :                 time_t *pwdlastset_update = NULL;
    3293             : 
    3294             :                 /*
    3295             :                  * obtain the proposed password
    3296             :                  */
    3297             : 
    3298             :                 /*
    3299             :                  * get the old token back.
    3300             :                  */
    3301             : 
    3302          12 :                 ret = pam_get_item(pamh, PAM_OLDAUTHTOK, (const void **) &pass_old);
    3303             : 
    3304          12 :                 if (ret != PAM_SUCCESS) {
    3305           0 :                         _pam_log(ctx, LOG_NOTICE,
    3306             :                                  "user not authenticated");
    3307           0 :                         goto out;
    3308             :                 }
    3309             : 
    3310          12 :                 lctrl = ctx->ctrl & ~WINBIND_TRY_FIRST_PASS_ARG;
    3311             : 
    3312          12 :                 if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
    3313           4 :                         lctrl |= WINBIND_USE_FIRST_PASS_ARG;
    3314             :                 }
    3315          12 :                 if (on(WINBIND_TRY_AUTHTOK_ARG, lctrl)) {
    3316           4 :                         lctrl |= WINBIND_TRY_FIRST_PASS_ARG;
    3317             :                 }
    3318          12 :                 retry = 0;
    3319          12 :                 ret = PAM_AUTHTOK_ERR;
    3320          30 :                 while ((ret != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
    3321             :                         /*
    3322             :                          * use_authtok is to force the use of a previously entered
    3323             :                          * password -- needed for pluggable password strength checking
    3324             :                          */
    3325             : 
    3326          12 :                         ret = _winbind_read_password(ctx, lctrl,
    3327             :                                                      NULL,
    3328          12 :                                                      _("Enter new NT password: "),
    3329          12 :                                                      _("Retype new NT password: "),
    3330             :                                                      (const char **)&pass_new);
    3331             : 
    3332          12 :                         if (ret != PAM_SUCCESS) {
    3333           0 :                                 _pam_log_debug(ctx, LOG_ALERT,
    3334             :                                                "password - "
    3335             :                                                "new password not obtained");
    3336           0 :                                 pass_old = NULL;/* tidy up */
    3337           0 :                                 goto out;
    3338             :                         }
    3339             : 
    3340             :                         /*
    3341             :                          * At this point we know who the user is and what they
    3342             :                          * propose as their new password. Verify that the new
    3343             :                          * password is acceptable.
    3344             :                          */
    3345             : 
    3346          12 :                         if (pass_new[0] == '\0') {/* "\0" password = NULL */
    3347           0 :                                 pass_new = NULL;
    3348             :                         }
    3349             :                 }
    3350             : 
    3351             :                 /*
    3352             :                  * By reaching here we have approved the passwords and must now
    3353             :                  * rebuild the password database file.
    3354             :                  */
    3355          12 :                 pam_get_data(pamh,
    3356             :                              PAM_WINBIND_PWD_LAST_SET,
    3357             :                              (const void **)&pwdlastset_update);
    3358             : 
    3359             :                 /*
    3360             :                  * if cached creds were enabled, make sure to set the
    3361             :                  * WINBIND_CACHED_LOGIN bit here in order to have winbindd
    3362             :                  * update the cached creds storage - gd
    3363             :                  */
    3364          12 :                 if (cached_login) {
    3365           0 :                         ctx->ctrl |= WINBIND_CACHED_LOGIN;
    3366             :                 }
    3367             : 
    3368          12 :                 ret = winbind_chauthtok_request(ctx, user, pass_old,
    3369             :                                                 pass_new, *pwdlastset_update);
    3370          12 :                 if (ret != PAM_SUCCESS) {
    3371           0 :                         pass_old = pass_new = NULL;
    3372           0 :                         goto out;
    3373             :                 }
    3374             : 
    3375          12 :                 if (_pam_require_krb5_auth_after_chauthtok(ctx, user)) {
    3376             : 
    3377           0 :                         const char *member = NULL;
    3378           0 :                         const char *cctype = NULL;
    3379             :                         int warn_pwd_expire;
    3380           0 :                         struct wbcLogonUserInfo *info = NULL;
    3381             : 
    3382           0 :                         member = get_member_from_config(ctx);
    3383           0 :                         cctype = get_krb5_cc_type_from_config(ctx);
    3384           0 :                         warn_pwd_expire = get_warn_pwd_expire_from_config(ctx);
    3385             : 
    3386             :                         /* Keep WINBIND_CACHED_LOGIN bit for
    3387             :                          * authentication after changing the password.
    3388             :                          * This will update the cached credentials in case
    3389             :                          * that winbindd_dual_pam_chauthtok() fails
    3390             :                          * to update them.
    3391             :                          * --- BoYang
    3392             :                          * */
    3393             : 
    3394           0 :                         ret = winbind_auth_request(ctx, user, pass_new,
    3395             :                                                    member, cctype, 0,
    3396             :                                                    &error, &info,
    3397             :                                                    NULL, &username_ret);
    3398           0 :                         pass_old = pass_new = NULL;
    3399             : 
    3400           0 :                         if (ret == PAM_SUCCESS) {
    3401             : 
    3402           0 :                                 struct wbcAuthUserInfo *user_info = NULL;
    3403             : 
    3404           0 :                                 if (info && info->info) {
    3405           0 :                                         user_info = info->info;
    3406             :                                 }
    3407             : 
    3408             :                                 /* warn a user if the password is about to
    3409             :                                  * expire soon */
    3410           0 :                                 _pam_warn_password_expiry(ctx, user_info,
    3411             :                                                           warn_pwd_expire,
    3412             :                                                           NULL, NULL);
    3413             : 
    3414             :                                 /* set some info3 info for other modules in the
    3415             :                                  * stack */
    3416           0 :                                 _pam_set_data_info3(ctx, user_info);
    3417             : 
    3418             :                                 /* put krb5ccname into env */
    3419           0 :                                 _pam_setup_krb5_env(ctx, info);
    3420             : 
    3421           0 :                                 if (username_ret) {
    3422           0 :                                         pam_set_item(pamh, PAM_USER,
    3423             :                                                      username_ret);
    3424           0 :                                         _pam_log_debug(ctx, LOG_INFO,
    3425             :                                                        "Returned user was '%s'",
    3426             :                                                        username_ret);
    3427           0 :                                         free(username_ret);
    3428             :                                 }
    3429             : 
    3430             :                         }
    3431             : 
    3432           0 :                         if (info && info->blobs) {
    3433           0 :                                 wbcFreeMemory(info->blobs);
    3434             :                         }
    3435           0 :                         wbcFreeMemory(info);
    3436             : 
    3437           0 :                         goto out;
    3438             :                 }
    3439             :         } else {
    3440           0 :                 ret = PAM_SERVICE_ERR;
    3441             :         }
    3442             : 
    3443          24 : out:
    3444             :         {
    3445             :                 /* Deal with offline errors. */
    3446             :                 int i;
    3447          24 :                 const char *codes[] = {
    3448             :                         "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
    3449             :                         "NT_STATUS_NO_LOGON_SERVERS",
    3450             :                         "NT_STATUS_ACCESS_DENIED"
    3451             :                 };
    3452             : 
    3453          96 :                 for (i=0; i<ARRAY_SIZE(codes); i++) {
    3454             :                         int _ret;
    3455          72 :                         if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) {
    3456           0 :                                 break;
    3457             :                         }
    3458             :                 }
    3459             :         }
    3460             : 
    3461          24 :         wbcFreeMemory(error);
    3462             : 
    3463          24 :         _PAM_LOG_FUNCTION_LEAVE("pam_sm_chauthtok", ctx, ret);
    3464             : 
    3465          24 :         TALLOC_FREE(ctx);
    3466             : 
    3467          24 :         return pam_error_code(ctx, PAM_WINBIND_CHAUTHTOK, ret);
    3468             : }
    3469             : 
    3470             : #ifdef PAM_STATIC
    3471             : 
    3472             : /* static module data */
    3473             : 
    3474             : struct pam_module _pam_winbind_modstruct = {
    3475             :         MODULE_NAME,
    3476             :         pam_sm_authenticate,
    3477             :         pam_sm_setcred,
    3478             :         pam_sm_acct_mgmt,
    3479             :         pam_sm_open_session,
    3480             :         pam_sm_close_session,
    3481             :         pam_sm_chauthtok
    3482             : };
    3483             : 
    3484             : #endif
    3485             : 
    3486             : /*
    3487             :  * Copyright (c) Andrew Tridgell  <tridge@samba.org>   2000
    3488             :  * Copyright (c) Tim Potter       <tpot@samba.org>     2000
    3489             :  * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
    3490             :  * Copyright (c) Guenther Deschner <gd@samba.org>      2005-2008
    3491             :  * Copyright (c) Jan Rêkorajski 1999.
    3492             :  * Copyright (c) Andrew G. Morgan 1996-8.
    3493             :  * Copyright (c) Alex O. Yuriev, 1996.
    3494             :  * Copyright (c) Cristian Gafton 1996.
    3495             :  * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software.
    3496             :  *
    3497             :  * Redistribution and use in source and binary forms, with or without
    3498             :  * modification, are permitted provided that the following conditions
    3499             :  * are met:
    3500             :  * 1. Redistributions of source code must retain the above copyright
    3501             :  *    notice, and the entire permission notice in its entirety,
    3502             :  *    including the disclaimer of warranties.
    3503             :  * 2. Redistributions in binary form must reproduce the above copyright
    3504             :  *    notice, this list of conditions and the following disclaimer in the
    3505             :  *    documentation and/or other materials provided with the distribution.
    3506             :  * 3. The name of the author may not be used to endorse or promote
    3507             :  *    products derived from this software without specific prior
    3508             :  *    written permission.
    3509             :  *
    3510             :  * ALTERNATIVELY, this product may be distributed under the terms of
    3511             :  * the GNU Public License, in which case the provisions of the GPL are
    3512             :  * required INSTEAD OF the above restrictions.  (This clause is
    3513             :  * necessary due to a potential bad interaction between the GPL and
    3514             :  * the restrictions contained in a BSD-style copyright.)
    3515             :  *
    3516             :  * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
    3517             :  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
    3518             :  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    3519             :  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
    3520             :  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    3521             :  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    3522             :  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    3523             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    3524             :  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    3525             :  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
    3526             :  * OF THE POSSIBILITY OF SUCH DAMAGE.
    3527             :  */

Generated by: LCOV version 1.13