nsswitch/pam_winbind.c

説明を見る。
00001 /* pam_winbind module
00002 
00003    Copyright Andrew Tridgell <tridge@samba.org> 2000
00004    Copyright Tim Potter <tpot@samba.org> 2000
00005    Copyright Andrew Bartlett <abartlet@samba.org> 2002
00006    Copyright Guenther Deschner <gd@samba.org> 2005-2007
00007 
00008    largely based on pam_userdb by Cristian Gafton <gafton@redhat.com> 
00009    also contains large slabs of code from pam_unix by Elliot Lee <sopwith@redhat.com>
00010    (see copyright below for full details)
00011 */
00012 
00013 #include "pam_winbind.h"
00014 
00015 #define _PAM_LOG_FUNCTION_ENTER(function, pamh, ctrl, flags) \
00016         do { \
00017                 _pam_log_debug(pamh, ctrl, LOG_DEBUG, "[pamh: 0x%08x] ENTER: " function " (flags: 0x%04x)", (uint32) pamh, flags); \
00018                 _pam_log_state(pamh, ctrl); \
00019         } while (0)
00020 
00021 #define _PAM_LOG_FUNCTION_LEAVE(function, pamh, ctrl, retval) \
00022         do { \
00023                 _pam_log_debug(pamh, ctrl, LOG_DEBUG, "[pamh: 0x%08x] LEAVE: " function " returning %d", (uint32) pamh, retval); \
00024                 _pam_log_state(pamh, ctrl); \
00025         } while (0)
00026 
00027 /* data tokens */
00028 
00029 #define MAX_PASSWD_TRIES        3
00030 
00031 /*
00032  * Work around the pam API that has functions with void ** as parameters.
00033  * These lead to strict aliasing warnings with gcc.
00034  */
00035 static int _pam_get_item(const pam_handle_t *pamh, int item_type,
00036                          const void *_item)
00037 {
00038         const void **item = (const void **)_item;
00039         return pam_get_item(pamh, item_type, item);
00040 }
00041 static int _pam_get_data(const pam_handle_t *pamh,
00042                          const char *module_data_name, const void *_data)
00043 {
00044         const void **data = (const void **)_data;
00045         return pam_get_data(pamh, module_data_name, data);
00046 }
00047 
00048 /* some syslogging */
00049 
00050 #ifdef HAVE_PAM_VSYSLOG
00051 static void _pam_log_int(const pam_handle_t *pamh, int err, const char *format, va_list args)
00052 {
00053         pam_vsyslog(pamh, err, format, args);
00054 }
00055 #else
00056 static void _pam_log_int(const pam_handle_t *pamh, int err, const char *format, va_list args)
00057 {
00058         char *format2 = NULL;
00059         const char *service;
00060 
00061         _pam_get_item(pamh, PAM_SERVICE, &service);
00062 
00063         format2 = malloc(strlen(MODULE_NAME)+strlen(format)+strlen(service)+5);
00064         if (format2 == NULL) {
00065                 /* what else todo ? */
00066                 vsyslog(err, format, args);
00067                 return;
00068         }
00069 
00070         sprintf(format2, "%s(%s): %s", MODULE_NAME, service, format);
00071         vsyslog(err, format2, args);
00072         SAFE_FREE(format2);
00073 }
00074 #endif /* HAVE_PAM_VSYSLOG */
00075 
00076 static BOOL _pam_log_is_silent(int ctrl)
00077 {
00078         return on(ctrl, WINBIND_SILENT);
00079 }
00080 
00081 static void _pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
00082 static void _pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...)
00083 {
00084         va_list args;
00085 
00086         if (_pam_log_is_silent(ctrl)) {
00087                 return;
00088         }
00089 
00090         va_start(args, format);
00091         _pam_log_int(pamh, err, format, args);
00092         va_end(args);
00093 }
00094 
00095 static BOOL _pam_log_is_debug_enabled(int ctrl)
00096 {
00097         if (ctrl == -1) {
00098                 return False;
00099         }
00100 
00101         if (_pam_log_is_silent(ctrl)) {
00102                 return False;
00103         }
00104 
00105         if (!(ctrl & WINBIND_DEBUG_ARG)) {
00106                 return False;
00107         }
00108 
00109         return True;
00110 }
00111 
00112 static BOOL _pam_log_is_debug_state_enabled(int ctrl)
00113 {
00114         if (!(ctrl & WINBIND_DEBUG_STATE)) {
00115                 return False;
00116         }
00117 
00118         return _pam_log_is_debug_enabled(ctrl);
00119 }
00120 
00121 static void _pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
00122 static void _pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...)
00123 {
00124         va_list args;
00125 
00126         if (!_pam_log_is_debug_enabled(ctrl)) {
00127                 return;
00128         }
00129 
00130         va_start(args, format);
00131         _pam_log_int(pamh, err, format, args);
00132         va_end(args);
00133 }
00134 
00135 static void _pam_log_state_datum(const pam_handle_t *pamh, int ctrl, int item_type, const char *key, int is_string)
00136 {
00137         const void *data = NULL;
00138         if (item_type != 0) {
00139                 pam_get_item(pamh, item_type, &data);
00140         } else {
00141                 pam_get_data(pamh, key, &data);
00142         }
00143         if (data != NULL) {
00144                 const char *type = (item_type != 0) ? "ITEM" : "DATA";
00145                 if (is_string != 0) {
00146                         _pam_log_debug(pamh, ctrl, LOG_DEBUG, "[pamh: 0x%08x] STATE: %s(%s) = \"%s\" (0x%08x)", (uint32) pamh, type, key, (const char *) data, (uint32) data);
00147                 } else {
00148                         _pam_log_debug(pamh, ctrl, LOG_DEBUG, "[pamh: 0x%08x] STATE: %s(%s) = 0x%08x", (uint32) pamh, type, key, (uint32) data);
00149                 }
00150         }
00151 }
00152 
00153 #define _PAM_LOG_STATE_DATA_POINTER(pamh, ctrl, module_data_name) \
00154         _pam_log_state_datum(pamh, ctrl, 0, module_data_name, 0)
00155 
00156 #define _PAM_LOG_STATE_DATA_STRING(pamh, ctrl, module_data_name) \
00157         _pam_log_state_datum(pamh, ctrl, 0, module_data_name, 1)
00158 
00159 #define _PAM_LOG_STATE_ITEM_POINTER(pamh, ctrl, item_type) \
00160         _pam_log_state_datum(pamh, ctrl, item_type, #item_type, 0)
00161 
00162 #define _PAM_LOG_STATE_ITEM_STRING(pamh, ctrl, item_type) \
00163         _pam_log_state_datum(pamh, ctrl, item_type, #item_type, 1)
00164 
00165 #ifdef DEBUG_PASSWORD
00166 #define _LOG_PASSWORD_AS_STRING 1
00167 #else
00168 #define _LOG_PASSWORD_AS_STRING 0
00169 #endif
00170 
00171 #define _PAM_LOG_STATE_ITEM_PASSWORD(pamh, ctrl, item_type) \
00172         _pam_log_state_datum(pamh, ctrl, item_type, #item_type, _LOG_PASSWORD_AS_STRING)
00173 
00174 static void _pam_log_state(const pam_handle_t *pamh, int ctrl)
00175 {
00176         if (!_pam_log_is_debug_state_enabled(ctrl)) {
00177                 return;
00178         }
00179 
00180         _PAM_LOG_STATE_ITEM_STRING(pamh, ctrl, PAM_SERVICE);
00181         _PAM_LOG_STATE_ITEM_STRING(pamh, ctrl, PAM_USER);
00182         _PAM_LOG_STATE_ITEM_STRING(pamh, ctrl, PAM_TTY);
00183         _PAM_LOG_STATE_ITEM_STRING(pamh, ctrl, PAM_RHOST);
00184         _PAM_LOG_STATE_ITEM_STRING(pamh, ctrl, PAM_RUSER);
00185         _PAM_LOG_STATE_ITEM_PASSWORD(pamh, ctrl, PAM_OLDAUTHTOK);
00186         _PAM_LOG_STATE_ITEM_PASSWORD(pamh, ctrl, PAM_AUTHTOK);
00187         _PAM_LOG_STATE_ITEM_STRING(pamh, ctrl, PAM_USER_PROMPT);
00188         _PAM_LOG_STATE_ITEM_POINTER(pamh, ctrl, PAM_CONV);
00189 #ifdef PAM_FAIL_DELAY
00190         _PAM_LOG_STATE_ITEM_POINTER(pamh, ctrl, PAM_FAIL_DELAY);
00191 #endif
00192 #ifdef PAM_REPOSITORY
00193         _PAM_LOG_STATE_ITEM_POINTER(pamh, ctrl, PAM_REPOSITORY);
00194 #endif
00195 
00196         _PAM_LOG_STATE_DATA_STRING(pamh, ctrl, PAM_WINBIND_HOMEDIR);
00197         _PAM_LOG_STATE_DATA_STRING(pamh, ctrl, PAM_WINBIND_LOGONSCRIPT);
00198         _PAM_LOG_STATE_DATA_STRING(pamh, ctrl, PAM_WINBIND_LOGONSERVER);
00199         _PAM_LOG_STATE_DATA_STRING(pamh, ctrl, PAM_WINBIND_PROFILEPATH);
00200         _PAM_LOG_STATE_DATA_STRING(pamh, ctrl, PAM_WINBIND_NEW_AUTHTOK_REQD); /* Use atoi to get PAM result code */
00201         _PAM_LOG_STATE_DATA_STRING(pamh, ctrl, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH);
00202         _PAM_LOG_STATE_DATA_POINTER(pamh, ctrl, PAM_WINBIND_PWD_LAST_SET);
00203 }
00204 
00205 static int _pam_parse(const pam_handle_t *pamh, int flags, int argc, const char **argv, dictionary **result_d)
00206 {
00207         int ctrl = 0;
00208         const char *config_file = NULL;
00209         int i;
00210         const char **v;
00211         dictionary *d = NULL;
00212 
00213         if (flags & PAM_SILENT) {
00214                 ctrl |= WINBIND_SILENT;
00215         }
00216 
00217         for (i=argc,v=argv; i-- > 0; ++v) {
00218                 if (!strncasecmp(*v, "config", strlen("config"))) {
00219                         ctrl |= WINBIND_CONFIG_FILE;
00220                         config_file = v[i];
00221                         break;
00222                 }
00223         }
00224 
00225         if (config_file == NULL) {
00226                 config_file = PAM_WINBIND_CONFIG_FILE;
00227         }
00228 
00229         d = iniparser_load(config_file);
00230         if (d == NULL) {
00231                 goto config_from_pam;
00232         }
00233 
00234         if (iniparser_getboolean(d, "global:debug", False)) {
00235                 ctrl |= WINBIND_DEBUG_ARG;
00236         }
00237 
00238         if (iniparser_getboolean(d, "global:debug_state", False)) {
00239                 ctrl |= WINBIND_DEBUG_STATE;
00240         }
00241 
00242         if (iniparser_getboolean(d, "global:cached_login", False)) {
00243                 ctrl |= WINBIND_CACHED_LOGIN;
00244         }
00245 
00246         if (iniparser_getboolean(d, "global:krb5_auth", False)) {
00247                 ctrl |= WINBIND_KRB5_AUTH;
00248         }
00249 
00250         if (iniparser_getboolean(d, "global:silent", False)) {
00251                 ctrl |= WINBIND_SILENT;
00252         }
00253 
00254         if (iniparser_getstr(d, "global:krb5_ccache_type") != NULL) {
00255                 ctrl |= WINBIND_KRB5_CCACHE_TYPE;
00256         }
00257 
00258         if ((iniparser_getstr(d, "global:require-membership-of") != NULL) ||
00259             (iniparser_getstr(d, "global:require_membership_of") != NULL)) {
00260                 ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
00261         }
00262 
00263         if (iniparser_getboolean(d, "global:try_first_pass", False)) {
00264                 ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
00265         }
00266 
00267 config_from_pam:
00268         /* step through arguments */
00269         for (i=argc,v=argv; i-- > 0; ++v) {
00270 
00271                 /* generic options */
00272                 if (!strcmp(*v,"debug"))
00273                         ctrl |= WINBIND_DEBUG_ARG;
00274                 else if (!strcasecmp(*v, "debug_state"))
00275                         ctrl |= WINBIND_DEBUG_STATE;
00276                 else if (!strcasecmp(*v, "use_authtok"))
00277                         ctrl |= WINBIND_USE_AUTHTOK_ARG;
00278                 else if (!strcasecmp(*v, "use_first_pass"))
00279                         ctrl |= WINBIND_USE_FIRST_PASS_ARG;
00280                 else if (!strcasecmp(*v, "try_first_pass"))
00281                         ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
00282                 else if (!strcasecmp(*v, "unknown_ok"))
00283                         ctrl |= WINBIND_UNKNOWN_OK_ARG;
00284                 else if (!strncasecmp(*v, "require_membership_of", strlen("require_membership_of")))
00285                         ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
00286                 else if (!strncasecmp(*v, "require-membership-of", strlen("require-membership-of")))
00287                         ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
00288                 else if (!strcasecmp(*v, "krb5_auth"))
00289                         ctrl |= WINBIND_KRB5_AUTH;
00290                 else if (!strncasecmp(*v, "krb5_ccache_type", strlen("krb5_ccache_type")))
00291                         ctrl |= WINBIND_KRB5_CCACHE_TYPE;
00292                 else if (!strcasecmp(*v, "cached_login"))
00293                         ctrl |= WINBIND_CACHED_LOGIN;
00294                 else {
00295                         _pam_log(pamh, ctrl, LOG_ERR, "pam_parse: unknown option: %s", *v);
00296                         return -1;
00297                 }
00298 
00299         }
00300 
00301         if (result_d) {
00302                 *result_d = d;
00303         } else {
00304                 if (d) {
00305                         iniparser_freedict(d);
00306                 }
00307         }
00308 
00309         return ctrl;
00310 };
00311 
00312 static void _pam_winbind_cleanup_func(pam_handle_t *pamh, void *data, int error_status)
00313 {
00314         int ctrl = _pam_parse(pamh, 0, 0, NULL, NULL);
00315         if (_pam_log_is_debug_state_enabled(ctrl)) {
00316                 _pam_log_debug(pamh, ctrl, LOG_DEBUG, "[pamh: 0x%08x] CLEAN: cleaning up PAM data 0x%08x (error_status = %d)", (uint32) pamh, (uint32) data, error_status);
00317         }
00318         SAFE_FREE(data);
00319 }
00320 
00321 
00322 static const struct ntstatus_errors {
00323         const char *ntstatus_string;
00324         const char *error_string;
00325 } ntstatus_errors[] = {
00326         {"NT_STATUS_OK", "Success"},
00327         {"NT_STATUS_BACKUP_CONTROLLER", "No primary Domain Controler available"},
00328         {"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", "No domain controllers found"},
00329         {"NT_STATUS_NO_LOGON_SERVERS", "No logon servers"},
00330         {"NT_STATUS_PWD_TOO_SHORT", "Password too short"},
00331         {"NT_STATUS_PWD_TOO_RECENT", "The password of this user is too recent to change"},
00332         {"NT_STATUS_PWD_HISTORY_CONFLICT", "Password is already in password history"},
00333         {"NT_STATUS_PASSWORD_EXPIRED", "Your password has expired"},
00334         {"NT_STATUS_PASSWORD_MUST_CHANGE", "You need to change your password now"},
00335         {"NT_STATUS_INVALID_WORKSTATION", "You are not allowed to logon from this workstation"},
00336         {"NT_STATUS_INVALID_LOGON_HOURS", "You are not allowed to logon at this time"},
00337         {"NT_STATUS_ACCOUNT_EXPIRED", "Your account has expired. Please contact your System administrator"}, /* SCNR */
00338         {"NT_STATUS_ACCOUNT_DISABLED", "Your account is disabled. Please contact your System administrator"}, /* SCNR */
00339         {"NT_STATUS_ACCOUNT_LOCKED_OUT", "Your account has been locked. Please contact your System administrator"}, /* SCNR */
00340         {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", "Invalid Trust Account"},
00341         {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", "Invalid Trust Account"},
00342         {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", "Invalid Trust Account"},
00343         {"NT_STATUS_ACCESS_DENIED", "Access is denied"},
00344         {NULL, NULL}
00345 };
00346 
00347 const char *_get_ntstatus_error_string(const char *nt_status_string) 
00348 {
00349         int i;
00350         for (i=0; ntstatus_errors[i].ntstatus_string != NULL; i++) {
00351                 if (!strcasecmp(ntstatus_errors[i].ntstatus_string, nt_status_string)) {
00352                         return ntstatus_errors[i].error_string;
00353                 }
00354         }
00355         return NULL;
00356 }
00357 
00358 /* --- authentication management functions --- */
00359 
00360 /* Attempt a conversation */
00361 
00362 static int converse(pam_handle_t *pamh, int nargs,
00363                     struct pam_message **message,
00364                     struct pam_response **response)
00365 {
00366         int retval;
00367         struct pam_conv *conv;
00368 
00369         retval = _pam_get_item(pamh, PAM_CONV, &conv );
00370         if (retval == PAM_SUCCESS) {
00371                 retval = conv->conv(nargs, (const struct pam_message **)message,
00372                                     response, conv->appdata_ptr);
00373         }
00374         
00375         return retval; /* propagate error status */
00376 }
00377 
00378 
00379 static int _make_remark(pam_handle_t * pamh, int flags, int type, const char *text)
00380 {
00381         int retval = PAM_SUCCESS;
00382 
00383         struct pam_message *pmsg[1], msg[1];
00384         struct pam_response *resp;
00385         
00386         if (flags & WINBIND_SILENT) {
00387                 return PAM_SUCCESS;
00388         }
00389 
00390         pmsg[0] = &msg[0];
00391         msg[0].msg = CONST_DISCARD(char *, text);
00392         msg[0].msg_style = type;
00393         
00394         resp = NULL;
00395         retval = converse(pamh, 1, pmsg, &resp);
00396         
00397         if (resp) {
00398                 _pam_drop_reply(resp, 1);
00399         }
00400         return retval;
00401 }
00402 
00403 static int _make_remark_v(pam_handle_t * pamh, int flags, int type, const char *format, va_list args)
00404 {
00405         char *var;
00406         int ret;
00407 
00408         ret = vasprintf(&var, format, args);
00409         if (ret < 0) {
00410                 _pam_log(pamh, 0, LOG_ERR, "memory allocation failure");
00411                 return ret;
00412         }
00413 
00414         ret = _make_remark(pamh, flags, type, var);
00415         SAFE_FREE(var);
00416         return ret;
00417 }
00418 
00419 static int _make_remark_format(pam_handle_t * pamh, int flags, int type, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
00420 static int _make_remark_format(pam_handle_t * pamh, int flags, int type, const char *format, ...)
00421 {
00422         int ret;
00423         va_list args;
00424 
00425         va_start(args, format);
00426         ret = _make_remark_v(pamh, flags, type, format, args);
00427         va_end(args);
00428         return ret;
00429 }
00430 
00431 static int pam_winbind_request(pam_handle_t * pamh, int ctrl,
00432                                enum winbindd_cmd req_type,
00433                                struct winbindd_request *request,
00434                                struct winbindd_response *response)
00435 {
00436         /* Fill in request and send down pipe */
00437         init_request(request, req_type);
00438         
00439         if (write_sock(request, sizeof(*request), 0, 0) == -1) {
00440                 _pam_log(pamh, ctrl, LOG_ERR, "pam_winbind_request: write to socket failed!");
00441                 close_sock();
00442                 return PAM_SERVICE_ERR;
00443         }
00444         
00445         /* Wait for reply */
00446         if (read_reply(response) == -1) {
00447                 _pam_log(pamh, ctrl, LOG_ERR, "pam_winbind_request: read from socket failed!");
00448                 close_sock();
00449                 return PAM_SERVICE_ERR;
00450         }
00451 
00452         /* We are done with the socket - close it and avoid mischeif */
00453         close_sock();
00454 
00455         /* Copy reply data from socket */
00456         if (response->result == WINBINDD_OK) {
00457                 return PAM_SUCCESS;
00458         }
00459 
00460         /* no need to check for pam_error codes for getpwnam() */
00461         switch (req_type) {
00462 
00463                 case WINBINDD_GETPWNAM:
00464                 case WINBINDD_LOOKUPNAME:
00465                         if (strlen(response->data.auth.nt_status_string) > 0) {
00466                                 _pam_log(pamh, ctrl, LOG_ERR, "request failed, NT error was %s", 
00467                                 response->data.auth.nt_status_string);
00468                         } else {
00469                                 _pam_log(pamh, ctrl, LOG_ERR, "request failed");
00470                         }
00471                         return PAM_USER_UNKNOWN;
00472                 default:
00473                         break;
00474         }
00475 
00476         if (response->data.auth.pam_error != PAM_SUCCESS) {
00477                 _pam_log(pamh, ctrl, LOG_ERR, "request failed: %s, PAM error was %s (%d), NT error was %s", 
00478                          response->data.auth.error_string,
00479                          pam_strerror(pamh, response->data.auth.pam_error),
00480                          response->data.auth.pam_error,
00481                          response->data.auth.nt_status_string);
00482                 return response->data.auth.pam_error;
00483         } 
00484         
00485         _pam_log(pamh, ctrl, LOG_ERR, "request failed, but PAM error 0!");
00486 
00487         return PAM_SERVICE_ERR;
00488 }
00489 
00490 static int pam_winbind_request_log(pam_handle_t * pamh,
00491                                    int ctrl,
00492                                    enum winbindd_cmd req_type,
00493                                    struct winbindd_request *request,
00494                                    struct winbindd_response *response,
00495                                    const char *user)
00496 {
00497         int retval;
00498 
00499         retval = pam_winbind_request(pamh, ctrl, req_type, request, response);
00500 
00501         switch (retval) {
00502         case PAM_AUTH_ERR:
00503                 /* incorrect password */
00504                 _pam_log(pamh, ctrl, LOG_WARNING, "user '%s' denied access (incorrect password or invalid membership)", user);
00505                 return retval;
00506         case PAM_ACCT_EXPIRED:
00507                 /* account expired */
00508                 _pam_log(pamh, ctrl, LOG_WARNING, "user '%s' account expired", user);
00509                 return retval;
00510         case PAM_AUTHTOK_EXPIRED:
00511                 /* password expired */
00512                 _pam_log(pamh, ctrl, LOG_WARNING, "user '%s' password expired", user);
00513                 return retval;
00514         case PAM_NEW_AUTHTOK_REQD:
00515                 /* new password required */
00516                 _pam_log(pamh, ctrl, LOG_WARNING, "user '%s' new password required", user);
00517                 return retval;
00518         case PAM_USER_UNKNOWN:
00519                 /* the user does not exist */
00520                 _pam_log_debug(pamh, ctrl, LOG_NOTICE, "user '%s' not found", user);
00521                 if (ctrl & WINBIND_UNKNOWN_OK_ARG) {
00522                         return PAM_IGNORE;
00523                 }        
00524                 return retval;
00525         case PAM_SUCCESS:
00526                 /* Otherwise, the authentication looked good */
00527                 switch (req_type) {
00528                         case WINBINDD_INFO:
00529                                 break;
00530                         case WINBINDD_PAM_AUTH:
00531                                 _pam_log(pamh, ctrl, LOG_NOTICE, "user '%s' granted access", user);
00532                                 break;
00533                         case WINBINDD_PAM_CHAUTHTOK:
00534                                 _pam_log(pamh, ctrl, LOG_NOTICE, "user '%s' password changed", user);
00535                                 break;
00536                         default:
00537                                 _pam_log(pamh, ctrl, LOG_NOTICE, "user '%s' OK", user);
00538                                 break;
00539                 }
00540         
00541                 return retval;
00542         default:
00543                 /* we don't know anything about this return value */
00544                 _pam_log(pamh, ctrl, LOG_ERR, "internal module error (retval = %d, user = '%s')",
00545                          retval, user);
00546                 return retval;
00547         }
00548 }
00549 
00550 /**
00551  * send a password expiry message if required
00552  * 
00553  * @param pamh PAM handle
00554  * @param ctrl PAM winbind options.
00555  * @param next_change expected (calculated) next expiry date.
00556  * @param already_expired pointer to a boolean to indicate if the password is
00557  *        already expired.
00558  *
00559  * @return boolean Returns True if message has been sent, False if not.
00560  */
00561 
00562 static BOOL _pam_send_password_expiry_message(pam_handle_t *pamh, int ctrl, time_t next_change, time_t now, BOOL *already_expired)
00563 {
00564         int days = 0;
00565         struct tm tm_now, tm_next_change;
00566 
00567         if (already_expired) {
00568                 *already_expired = False;
00569         }
00570 
00571         if (next_change <= now) {
00572                 PAM_WB_REMARK_DIRECT(pamh, ctrl, "NT_STATUS_PASSWORD_EXPIRED");
00573                 if (already_expired) {
00574                         *already_expired = True;
00575                 }
00576                 return True;
00577         }
00578 
00579         if ((next_change < 0) ||
00580             (next_change > now + DAYS_TO_WARN_BEFORE_PWD_EXPIRES * SECONDS_PER_DAY)) {
00581                 return False;
00582         }
00583 
00584         if ((localtime_r(&now, &tm_now) == NULL) || 
00585             (localtime_r(&next_change, &tm_next_change) == NULL)) {
00586                 return False;
00587         }
00588 
00589         days = (tm_next_change.tm_yday+tm_next_change.tm_year*365) - (tm_now.tm_yday+tm_now.tm_year*365);
00590 
00591         if (days == 0) {
00592                 _make_remark(pamh, ctrl, PAM_TEXT_INFO, "Your password expires today");
00593                 return True;
00594         } 
00595         
00596         if (days > 0 && days < DAYS_TO_WARN_BEFORE_PWD_EXPIRES) {
00597                 _make_remark_format(pamh, ctrl, PAM_TEXT_INFO, "Your password will expire in %d %s", 
00598                         days, (days > 1) ? "days":"day");
00599                 return True;
00600         }
00601 
00602         return False;
00603 }
00604 
00605 /**
00606  * Send a warning if the password expires in the near future
00607  *
00608  * @param pamh PAM handle
00609  * @param ctrl PAM winbind options.
00610  * @param response The full authentication response structure.
00611  * @param already_expired boolean, is the pwd already expired?
00612  *
00613  * @return void.
00614  */
00615 
00616 static void _pam_warn_password_expiry(pam_handle_t *pamh, 
00617                                       int flags, 
00618                                       const struct winbindd_response *response,
00619                                       BOOL *already_expired)
00620 {
00621         time_t now = time(NULL);
00622         time_t next_change = 0;
00623 
00624         if (already_expired) {
00625                 *already_expired = False;
00626         }
00627 
00628         /* accounts with ACB_PWNOEXP set never receive a warning */
00629         if (response->data.auth.info3.acct_flags & ACB_PWNOEXP) {
00630                 return;
00631         }
00632 
00633         /* no point in sending a warning if this is a grace logon */
00634         if (PAM_WB_GRACE_LOGON(response->data.auth.info3.user_flgs)) {
00635                 return;
00636         }
00637 
00638         /* check if the info3 must change timestamp has been set */
00639         next_change = response->data.auth.info3.pass_must_change_time;
00640 
00641         if (_pam_send_password_expiry_message(pamh, flags, next_change, now, 
00642                                               already_expired)) {
00643                 return;
00644         }
00645 
00646         /* now check for the global password policy */
00647         /* good catch from Ralf Haferkamp: an expiry of "never" is translated
00648          * to -1 */
00649         if (response->data.auth.policy.expire <= 0) {
00650                 return;
00651         }
00652 
00653         next_change = response->data.auth.info3.pass_last_set_time + 
00654                       response->data.auth.policy.expire;
00655 
00656         if (_pam_send_password_expiry_message(pamh, flags, next_change, now, 
00657                                               already_expired)) {
00658                 return;
00659         }
00660 
00661         /* no warning sent */
00662 }
00663 
00664 #define IS_SID_STRING(name) (strncmp("S-", name, 2) == 0)
00665 
00666 static BOOL safe_append_string(char *dest,
00667                         const char *src,
00668                         int dest_buffer_size)
00669 /**
00670  * Append a string, making sure not to overflow and to always return a NULL-terminated
00671  * string.
00672  *
00673  * @param dest Destination string buffer (must already be NULL-terminated).
00674  * @param src Source string buffer.
00675  * @param dest_buffer_size Size of dest buffer in bytes.
00676  *
00677  * @return False if dest buffer is not big enough (no bytes copied), True on success.
00678  */
00679 {
00680         int dest_length = strlen(dest);
00681         int src_length = strlen(src);
00682 
00683         if ( dest_length + src_length + 1 > dest_buffer_size ) {
00684                 return False;
00685         }
00686 
00687         memcpy(dest + dest_length, src, src_length + 1);
00688         return True;
00689 }
00690 
00691 static BOOL winbind_name_to_sid_string(pam_handle_t *pamh,
00692                                 int ctrl,
00693                                 const char *user,
00694                                 const char *name,
00695                                 char *sid_list_buffer,
00696                                 int sid_list_buffer_size)
00697 /**
00698  * Convert a names into a SID string, appending it to a buffer.
00699  *
00700  * @param pamh PAM handle
00701  * @param ctrl PAM winbind options.
00702  * @param user User in PAM request.
00703  * @param name Name to convert.
00704  * @param sid_list_buffer Where to append the string sid.
00705  * @param sid_list_buffer Size of sid_list_buffer (in bytes).
00706  *
00707  * @return False on failure, True on success.
00708  */
00709 {
00710         const char* sid_string;
00711         struct winbindd_response sid_response;
00712 
00713         /* lookup name? */ 
00714         if (IS_SID_STRING(name)) {
00715                 sid_string = name;
00716         } else {
00717                 struct winbindd_request sid_request;
00718 
00719                 ZERO_STRUCT(sid_request);
00720                 ZERO_STRUCT(sid_response);
00721 
00722                 _pam_log_debug(pamh, ctrl, LOG_DEBUG, "no sid given, looking up: %s\n", name);
00723 
00724                 /* fortunatly winbindd can handle non-separated names */
00725                 strncpy(sid_request.data.name.name, name,
00726                         sizeof(sid_request.data.name.name) - 1);
00727 
00728                 if (pam_winbind_request_log(pamh, ctrl, WINBINDD_LOOKUPNAME, &sid_request, &sid_response, user)) {
00729                         _pam_log(pamh, ctrl, LOG_INFO, "could not lookup name: %s\n", name); 
00730                         return False;
00731                 }
00732 
00733                 sid_string = sid_response.data.sid.sid;
00734         }
00735 
00736         if (!safe_append_string(sid_list_buffer, sid_string, sid_list_buffer_size)) {
00737                 return False;
00738         }
00739 
00740         return True;
00741 }
00742 
00743 static BOOL winbind_name_list_to_sid_string_list(pam_handle_t *pamh,
00744                                 int ctrl,
00745                                 const char *user,
00746                                 const char *name_list,
00747                                 char *sid_list_buffer,
00748                                 int sid_list_buffer_size)
00749 /**
00750  * Convert a list of names into a list of sids.
00751  *
00752  * @param pamh PAM handle
00753  * @param ctrl PAM winbind options.
00754  * @param user User in PAM request.
00755  * @param name_list List of names or string sids, separated by commas.
00756  * @param sid_list_buffer Where to put the list of string sids.
00757  * @param sid_list_buffer Size of sid_list_buffer (in bytes).
00758  *
00759  * @return False on failure, True on success.
00760  */
00761 {
00762         BOOL result = False;
00763         char *current_name = NULL;
00764         const char *search_location;
00765         const char *comma;
00766 
00767         if ( sid_list_buffer_size > 0 ) {
00768                 sid_list_buffer[0] = 0;
00769         }
00770 
00771         search_location = name_list;
00772         while ( (comma = strstr(search_location, ",")) != NULL ) {
00773                 current_name = strndup(search_location, comma - search_location);
00774                 if (NULL == current_name) {
00775                         goto out;
00776                 }
00777 
00778                 if (!winbind_name_to_sid_string(pamh, ctrl, user, current_name, sid_list_buffer, sid_list_buffer_size)) {
00779                         goto out;
00780                 }
00781 
00782                 SAFE_FREE(current_name);
00783 
00784                 if (!safe_append_string(sid_list_buffer, ",", sid_list_buffer_size)) {
00785                         goto out;
00786                 }
00787 
00788                 search_location = comma + 1;
00789         }
00790 
00791         if (!winbind_name_to_sid_string(pamh, ctrl, user, search_location, sid_list_buffer, sid_list_buffer_size)) {
00792                 goto out;
00793         }
00794 
00795         result = True;
00796 
00797 out:
00798         SAFE_FREE(current_name);
00799         return result;
00800 }
00801 
00802 /**
00803  * put krb5ccname variable into environment
00804  *
00805  * @param pamh PAM handle
00806  * @param ctrl PAM winbind options.
00807  * @param krb5ccname env variable retrieved from winbindd.
00808  *
00809  * @return void.
00810  */
00811 
00812 static void _pam_setup_krb5_env(pam_handle_t *pamh, int ctrl, const char *krb5ccname)
00813 {
00814         char var[PATH_MAX];
00815         int ret;
00816 
00817         if (off(ctrl, WINBIND_KRB5_AUTH)) {
00818                 return;
00819         }
00820 
00821         if (!krb5ccname || (strlen(krb5ccname) == 0)) {
00822                 return;
00823         }
00824 
00825         _pam_log_debug(pamh, ctrl, LOG_DEBUG, "request returned KRB5CCNAME: %s", krb5ccname);
00826         
00827         if (snprintf(var, sizeof(var), "KRB5CCNAME=%s", krb5ccname) == -1) {
00828                 return;
00829         }
00830         
00831         ret = pam_putenv(pamh, var);
00832         if (ret) {
00833                 _pam_log(pamh, ctrl, LOG_ERR, "failed to set KRB5CCNAME to %s: %s", 
00834                         var, pam_strerror(pamh, ret));
00835         }
00836 }       
00837 
00838 /**
00839  * Set string into the PAM stack.
00840  *
00841  * @param pamh PAM handle
00842  * @param ctrl PAM winbind options.
00843  * @param data_name Key name for pam_set_data.
00844  * @param value String value.
00845  *
00846  * @return void.
00847  */
00848 
00849 static void _pam_set_data_string(pam_handle_t *pamh, int ctrl, const char *data_name, const char *value)
00850 {
00851         int ret;
00852 
00853         if ( !data_name || !value || (strlen(data_name) == 0) || (strlen(value) == 0) ) {
00854                 return;
00855         }
00856 
00857         ret = pam_set_data(pamh, data_name, (void *)strdup(value), _pam_winbind_cleanup_func);
00858         if (ret) {
00859                 _pam_log_debug(pamh, ctrl, LOG_DEBUG, "Could not set data %s: %s\n", 
00860                         data_name, pam_strerror(pamh, ret));
00861         }
00862 
00863 }
00864 
00865 /**
00866  * Set info3 strings into the PAM stack.
00867  *
00868  * @param pamh PAM handle
00869  * @param ctrl PAM winbind options.
00870  * @param data_name Key name for pam_set_data.
00871  * @param value String value.
00872  *
00873  * @return void.
00874  */
00875 
00876 static void _pam_set_data_info3(pam_handle_t *pamh, int ctrl, struct winbindd_response *response)
00877 {
00878         _pam_set_data_string(pamh, ctrl, PAM_WINBIND_HOMEDIR, response->data.auth.info3.home_dir);
00879         _pam_set_data_string(pamh, ctrl, PAM_WINBIND_LOGONSCRIPT, response->data.auth.info3.logon_script);
00880         _pam_set_data_string(pamh, ctrl, PAM_WINBIND_LOGONSERVER, response->data.auth.info3.logon_srv);
00881         _pam_set_data_string(pamh, ctrl, PAM_WINBIND_PROFILEPATH, response->data.auth.info3.profile_path);
00882 }
00883 
00884 /**
00885  * Free info3 strings in the PAM stack.
00886  *
00887  * @param pamh PAM handle
00888  *
00889  * @return void.
00890  */
00891 
00892 static void _pam_free_data_info3(pam_handle_t *pamh)
00893 {
00894         pam_set_data(pamh, PAM_WINBIND_HOMEDIR, NULL, NULL);
00895         pam_set_data(pamh, PAM_WINBIND_LOGONSCRIPT, NULL, NULL);
00896         pam_set_data(pamh, PAM_WINBIND_LOGONSERVER, NULL, NULL);
00897         pam_set_data(pamh, PAM_WINBIND_PROFILEPATH, NULL, NULL);
00898 }
00899 
00900 /**
00901  * Send PAM_ERROR_MSG for cached or grace logons.
00902  *
00903  * @param pamh PAM handle
00904  * @param ctrl PAM winbind options.
00905  * @param username User in PAM request.
00906  * @param info3_user_flgs Info3 flags containing logon type bits.
00907  *
00908  * @return void.
00909  */
00910 
00911 static void _pam_warn_logon_type(pam_handle_t *pamh, int ctrl, const char *username, uint32 info3_user_flgs)
00912 {
00913         /* inform about logon type */
00914         if (PAM_WB_GRACE_LOGON(info3_user_flgs)) {
00915 
00916                 _make_remark(pamh, ctrl, PAM_ERROR_MSG, 
00917                         "Grace login. Please change your password as soon you're online again");
00918                 _pam_log_debug(pamh, ctrl, LOG_DEBUG,
00919                         "User %s logged on using grace logon\n", username);
00920 
00921         } else if (PAM_WB_CACHED_LOGON(info3_user_flgs)) {
00922 
00923                 _make_remark(pamh, ctrl, PAM_ERROR_MSG, 
00924                         "Domain Controller unreachable, using cached credentials instead. Network resources may be unavailable");
00925                 _pam_log_debug(pamh, ctrl, LOG_DEBUG,
00926                         "User %s logged on using cached credentials\n", username);
00927         }
00928 }
00929 
00930 /**
00931  * Compose Password Restriction String for a PAM_ERROR_MSG conversation.
00932  *
00933  * @param response The struct winbindd_response.
00934  *
00935  * @return string (caller needs to free).
00936  */
00937 
00938 static char *_pam_compose_pwd_restriction_string(struct winbindd_response *response)
00939 {
00940         char *str = NULL;
00941         size_t offset = 0, ret = 0, str_size = 1024;
00942 
00943         str = (char *)malloc(str_size);
00944         if (!str) {
00945                 return NULL;
00946         }
00947 
00948         memset(str, '\0', str_size);
00949 
00950         offset = snprintf(str, str_size, "Your password ");
00951         if (offset == -1) {
00952                 goto failed;
00953         }
00954 
00955         if (response->data.auth.policy.min_length_password > 0) {
00956                 ret = snprintf(str+offset, str_size-offset,
00957                              "must be at least %d characters; ",
00958                              response->data.auth.policy.min_length_password);
00959                 if (ret == -1) {
00960                         goto failed;
00961                 }
00962                 offset += ret;
00963         }
00964         
00965         if (response->data.auth.policy.password_history > 0) {
00966                 ret = snprintf(str+offset, str_size-offset,
00967                              "cannot repeat any of your previous %d passwords; ",
00968                              response->data.auth.policy.password_history);
00969                 if (ret == -1) {
00970                         goto failed;
00971                 }
00972                 offset += ret;
00973         }
00974         
00975         if (response->data.auth.policy.password_properties & DOMAIN_PASSWORD_COMPLEX) {
00976                 ret = snprintf(str+offset, str_size-offset,
00977                              "must contain capitals, numerals or punctuation; "
00978                              "and cannot contain your account or full name; ");
00979                 if (ret == -1) {
00980                         goto failed;
00981                 }
00982                 offset += ret;
00983         }
00984 
00985         ret = snprintf(str+offset, str_size-offset, 
00986                      "Please type a different password. "
00987                      "Type a password which meets these requirements in both text boxes.");
00988         if (ret == -1) {
00989                 goto failed;
00990         }
00991 
00992         return str;
00993 
00994  failed:
00995         SAFE_FREE(str);
00996         return NULL;
00997 }
00998 
00999 /* talk to winbindd */
01000 static int winbind_auth_request(pam_handle_t * pamh,
01001                                 int ctrl, 
01002                                 const char *user, 
01003                                 const char *pass, 
01004                                 const char *member, 
01005                                 const char *cctype,
01006                                 struct winbindd_response *p_response,
01007                                 time_t *pwd_last_set,
01008                                 char **user_ret)
01009 {
01010         struct winbindd_request request;
01011         struct winbindd_response response;
01012         int ret;
01013         BOOL already_expired = False;
01014 
01015         ZERO_STRUCT(request);
01016         ZERO_STRUCT(response);
01017 
01018         if (pwd_last_set) {
01019                 *pwd_last_set = 0;
01020         }
01021 
01022         strncpy(request.data.auth.user, user, 
01023                 sizeof(request.data.auth.user)-1);
01024 
01025         strncpy(request.data.auth.pass, pass, 
01026                 sizeof(request.data.auth.pass)-1);
01027 
01028         request.data.auth.krb5_cc_type[0] = '\0';
01029         request.data.auth.uid = -1;
01030         
01031         request.flags = WBFLAG_PAM_INFO3_TEXT |
01032                         WBFLAG_PAM_GET_PWD_POLICY |
01033                         WBFLAG_PAM_CONTACT_TRUSTDOM;
01034 
01035         if (ctrl & (WINBIND_KRB5_AUTH|WINBIND_CACHED_LOGIN)) {
01036                 struct passwd *pwd = NULL;
01037 
01038                 pwd = getpwnam(user);
01039                 if (pwd == NULL) {
01040                         return PAM_USER_UNKNOWN;
01041                 }
01042                 request.data.auth.uid = pwd->pw_uid;
01043         }
01044 
01045         if (ctrl & WINBIND_KRB5_AUTH) {
01046 
01047                 _pam_log_debug(pamh, ctrl, LOG_DEBUG, "enabling krb5 login flag\n"); 
01048 
01049                 request.flags |= WBFLAG_PAM_KRB5 | WBFLAG_PAM_FALLBACK_AFTER_KRB5;
01050         }
01051 
01052         if (ctrl & WINBIND_CACHED_LOGIN) {
01053                 _pam_log_debug(pamh, ctrl, LOG_DEBUG, "enabling cached login flag\n"); 
01054                 request.flags |= WBFLAG_PAM_CACHED_LOGIN;
01055         }
01056 
01057         if (user_ret) {
01058                 *user_ret = NULL;
01059                 request.flags |= WBFLAG_PAM_UNIX_NAME;
01060         }
01061 
01062         if (cctype != NULL) {
01063                 strncpy(request.data.auth.krb5_cc_type, cctype, 
01064                         sizeof(request.data.auth.krb5_cc_type) - 1);
01065                 _pam_log_debug(pamh, ctrl, LOG_DEBUG, "enabling request for a %s krb5 ccache\n", cctype); 
01066         }
01067 
01068         request.data.auth.require_membership_of_sid[0] = '\0';
01069 
01070         if (member != NULL) {
01071 
01072                 if (!winbind_name_list_to_sid_string_list(pamh, ctrl, user, member,
01073                         request.data.auth.require_membership_of_sid,
01074                         sizeof(request.data.auth.require_membership_of_sid))) {
01075 
01076                         _pam_log_debug(pamh, ctrl, LOG_ERR, "failed to serialize membership of sid \"%s\"\n", member);
01077                         return PAM_AUTH_ERR;
01078                 }
01079         }
01080 
01081         ret = pam_winbind_request_log(pamh, ctrl, WINBINDD_PAM_AUTH, &request, &response, user);
01082 
01083         if (pwd_last_set) {
01084                 *pwd_last_set = response.data.auth.info3.pass_last_set_time;
01085         }
01086 
01087         if (p_response) {
01088                 /* We want to process the response in the caller. */
01089                 *p_response = response;
01090                 return ret;
01091         }
01092 
01093         if (ret) {
01094                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_PASSWORD_EXPIRED");
01095                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_PASSWORD_MUST_CHANGE");
01096                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_INVALID_WORKSTATION");
01097                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_INVALID_LOGON_HOURS");
01098                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_ACCOUNT_EXPIRED");
01099                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_ACCOUNT_DISABLED");
01100                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_ACCOUNT_LOCKED_OUT");
01101                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT");
01102                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT");
01103                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT");
01104                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND");
01105                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_NO_LOGON_SERVERS");
01106                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_WRONG_PASSWORD");
01107                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_ACCESS_DENIED");
01108         }
01109 
01110         if (ret == PAM_SUCCESS) {
01111 
01112                 /* warn a user if the password is about to expire soon */
01113                 _pam_warn_password_expiry(pamh, ctrl, &response, &already_expired);
01114 
01115                 if (already_expired == True) {
01116                         _pam_log_debug(pamh, ctrl, LOG_DEBUG, "Password has expired "
01117                                        "(Password was last set: %lld, the policy says "
01118                                        "it should expire here %lld (now it's: %lu))\n",
01119                                        response.data.auth.info3.pass_last_set_time, 
01120                                        response.data.auth.info3.pass_last_set_time +
01121                                        response.data.auth.policy.expire,
01122                                        time(NULL));
01123 
01124                         return PAM_AUTHTOK_EXPIRED;
01125                 }
01126 
01127                 /* inform about logon type */
01128                 _pam_warn_logon_type(pamh, ctrl, user, response.data.auth.info3.user_flgs);
01129 
01130                 /* set some info3 info for other modules in the stack */
01131                 _pam_set_data_info3(pamh, ctrl, &response);
01132 
01133                 /* put krb5ccname into env */
01134                 _pam_setup_krb5_env(pamh, ctrl, response.data.auth.krb5ccname);
01135 
01136                 /* If winbindd returned a username, return the pointer to it here. */
01137                 if (user_ret && response.extra_data.data) {
01138                         /* We have to trust it's a null terminated string. */
01139                         *user_ret = (char *)response.extra_data.data;
01140                 }
01141         }
01142 
01143         return ret;
01144 }
01145 
01146 /* talk to winbindd */
01147 static int winbind_chauthtok_request(pam_handle_t * pamh,
01148                                      int ctrl,
01149                                      const char *user, 
01150                                      const char *oldpass,
01151                                      const char *newpass,
01152                                      time_t pwd_last_set) 
01153 {
01154         struct winbindd_request request;
01155         struct winbindd_response response;
01156         int ret;
01157 
01158         ZERO_STRUCT(request);
01159         ZERO_STRUCT(response);
01160 
01161         if (request.data.chauthtok.user == NULL) return -2;
01162 
01163         strncpy(request.data.chauthtok.user, user, 
01164                 sizeof(request.data.chauthtok.user) - 1);
01165 
01166         if (oldpass != NULL) {
01167                 strncpy(request.data.chauthtok.oldpass, oldpass, 
01168                         sizeof(request.data.chauthtok.oldpass) - 1);
01169         } else {
01170                 request.data.chauthtok.oldpass[0] = '\0';
01171         }
01172         
01173         if (newpass != NULL) {
01174                 strncpy(request.data.chauthtok.newpass, newpass, 
01175                         sizeof(request.data.chauthtok.newpass) - 1);
01176         } else {
01177                 request.data.chauthtok.newpass[0] = '\0';
01178         }
01179 
01180         if (ctrl & WINBIND_KRB5_AUTH) {
01181                 request.flags = WBFLAG_PAM_KRB5 | WBFLAG_PAM_CONTACT_TRUSTDOM;
01182         }
01183 
01184         if (ctrl & WINBIND_CACHED_LOGIN) {
01185                 request.flags |= WBFLAG_PAM_CACHED_LOGIN;
01186         }
01187 
01188         ret = pam_winbind_request_log(pamh, ctrl, WINBINDD_PAM_CHAUTHTOK, &request, &response, user);
01189 
01190         if (ret == PAM_SUCCESS) {
01191                 return ret;
01192         }
01193 
01194         PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_BACKUP_CONTROLLER");
01195         PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND");
01196         PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_NO_LOGON_SERVERS");
01197         PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_ACCESS_DENIED");
01198 
01199         /* TODO: tell the min pwd length ? */
01200         PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_PWD_TOO_SHORT");
01201 
01202         /* TODO: tell the minage ? */
01203         PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_PWD_TOO_RECENT");
01204 
01205         /* TODO: tell the history length ? */
01206         PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_PWD_HISTORY_CONFLICT");
01207 
01208         if (!strcasecmp(response.data.auth.nt_status_string, "NT_STATUS_PASSWORD_RESTRICTION")) {
01209 
01210                 char *pwd_restriction_string = NULL;
01211 
01212                 /* FIXME: avoid to send multiple PAM messages after another */
01213                 switch (response.data.auth.reject_reason) {
01214                         case -1:
01215                                 break;
01216                         case REJECT_REASON_OTHER:
01217                                 if ((response.data.auth.policy.min_passwordage > 0) &&
01218                                     (pwd_last_set + response.data.auth.policy.min_passwordage > time(NULL))) {
01219                                         PAM_WB_REMARK_DIRECT(pamh, ctrl, "NT_STATUS_PWD_TOO_RECENT");
01220                                 }
01221                                 break;
01222                         case REJECT_REASON_TOO_SHORT:
01223                                 PAM_WB_REMARK_DIRECT(pamh, ctrl, "NT_STATUS_PWD_TOO_SHORT");
01224                                 break;
01225                         case REJECT_REASON_IN_HISTORY:
01226                                 PAM_WB_REMARK_DIRECT(pamh, ctrl, "NT_STATUS_PWD_HISTORY_CONFLICT");
01227                                 break;
01228                         case REJECT_REASON_NOT_COMPLEX:
01229                                 _make_remark(pamh, ctrl, PAM_ERROR_MSG, "Password does not meet complexity requirements");
01230                                 break;
01231                         default:
01232                                 _pam_log_debug(pamh, ctrl, LOG_DEBUG,
01233                                                "unknown password change reject reason: %d", 
01234                                                response.data.auth.reject_reason);
01235                                 break;
01236                 }
01237 
01238                 pwd_restriction_string = _pam_compose_pwd_restriction_string(&response);
01239                 if (pwd_restriction_string) {
01240                         _make_remark(pamh, ctrl, PAM_ERROR_MSG, pwd_restriction_string);
01241                         SAFE_FREE(pwd_restriction_string);
01242                 }
01243         }
01244 
01245         return ret;
01246 }
01247 
01248 /*
01249  * Checks if a user has an account
01250  *
01251  * return values:
01252  *       1  = User not found
01253  *       0  = OK
01254  *      -1  = System error
01255  */
01256 static int valid_user(pam_handle_t *pamh, int ctrl, const char *user)
01257 {
01258         /* check not only if the user is available over NSS calls, also make
01259          * sure it's really a winbind user, this is important when stacking PAM
01260          * modules in the 'account' or 'password' facility. */
01261 
01262         struct passwd *pwd = NULL;
01263         struct winbindd_request request;
01264         struct winbindd_response response;
01265         int ret;
01266 
01267         ZERO_STRUCT(request);
01268         ZERO_STRUCT(response);
01269 
01270         pwd = getpwnam(user);
01271         if (pwd == NULL) {
01272                 return 1;
01273         }
01274 
01275         strncpy(request.data.username, user,
01276                 sizeof(request.data.username) - 1);
01277 
01278         ret = pam_winbind_request_log(pamh, ctrl, WINBINDD_GETPWNAM, &request, &response, user);
01279 
01280         switch (ret) {
01281                 case PAM_USER_UNKNOWN:
01282                         return 1;
01283                 case PAM_SUCCESS:
01284                         return 0;
01285                 default:
01286                         break;
01287         }
01288         return -1;
01289 }
01290 
01291 static char *_pam_delete(register char *xx)
01292 {
01293         _pam_overwrite(xx);
01294         _pam_drop(xx);
01295         return NULL;
01296 }
01297 
01298 /*
01299  * obtain a password from the user
01300  */
01301 
01302 static int _winbind_read_password(pam_handle_t * pamh,
01303                                   unsigned int ctrl,
01304                                   const char *comment,
01305                                   const char *prompt1,
01306                                   const char *prompt2,
01307                                   const char **pass)
01308 {
01309         int authtok_flag;
01310         int retval;
01311         const char *item;
01312         char *token;
01313 
01314         _pam_log(pamh, ctrl, LOG_DEBUG, "getting password (0x%08x)", ctrl);
01315 
01316         /*
01317          * make sure nothing inappropriate gets returned
01318          */
01319 
01320         *pass = token = NULL;
01321 
01322         /*
01323          * which authentication token are we getting?
01324          */
01325 
01326         authtok_flag = on(WINBIND__OLD_PASSWORD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
01327 
01328         /*
01329          * should we obtain the password from a PAM item ?
01330          */
01331 
01332         if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) || on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
01333                 retval = _pam_get_item(pamh, authtok_flag, &item);
01334                 if (retval != PAM_SUCCESS) {
01335                         /* very strange. */
01336                         _pam_log(pamh, ctrl, LOG_ALERT, 
01337                                  "pam_get_item returned error to unix-read-password"
01338                             );
01339                         return retval;
01340                 } else if (item != NULL) {      /* we have a password! */
01341                         *pass = item;
01342                         item = NULL;
01343                         _pam_log(pamh, ctrl, LOG_DEBUG, 
01344                                  "pam_get_item returned a password");
01345                         return PAM_SUCCESS;
01346                 } else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
01347                         return PAM_AUTHTOK_RECOVER_ERR;         /* didn't work */
01348                 } else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl)
01349                            && off(WINBIND__OLD_PASSWORD, ctrl)) {
01350                         return PAM_AUTHTOK_RECOVER_ERR;
01351                 }
01352         }
01353         /*
01354          * getting here implies we will have to get the password from the
01355          * user directly.
01356          */
01357 
01358         {
01359                 struct pam_message msg[3], *pmsg[3];
01360                 struct pam_response *resp;
01361                 int i, replies;
01362 
01363                 /* prepare to converse */
01364 
01365                 if (comment != NULL && off(ctrl, WINBIND_SILENT)) {
01366                         pmsg[0] = &msg[0];
01367                         msg[0].msg_style = PAM_TEXT_INFO;
01368                         msg[0].msg = CONST_DISCARD(char *, comment);
01369                         i = 1;
01370                 } else {
01371                         i = 0;
01372                 }
01373 
01374                 pmsg[i] = &msg[i];
01375                 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
01376                 msg[i++].msg = CONST_DISCARD(char *, prompt1);
01377                 replies = 1;
01378 
01379                 if (prompt2 != NULL) {
01380                         pmsg[i] = &msg[i];
01381                         msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
01382                         msg[i++].msg = CONST_DISCARD(char *, prompt2);
01383                         ++replies;
01384                 }
01385                 /* so call the conversation expecting i responses */
01386                 resp = NULL;
01387                 retval = converse(pamh, i, pmsg, &resp);
01388 
01389                 if (resp != NULL) {
01390 
01391                         /* interpret the response */
01392 
01393                         if (retval == PAM_SUCCESS) {    /* a good conversation */
01394 
01395                                 token = x_strdup(resp[i - replies].resp);
01396                                 if (token != NULL) {
01397                                         if (replies == 2) {
01398                                                 /* verify that password entered correctly */
01399                                                 if (!resp[i - 1].resp
01400                                                     || strcmp(token, resp[i - 1].resp)) {
01401                                                         _pam_delete(token);     /* mistyped */
01402                                                         retval = PAM_AUTHTOK_RECOVER_ERR;
01403                                                         _make_remark(pamh, ctrl, PAM_ERROR_MSG, MISTYPED_PASS);
01404                                                 }
01405                                         }
01406                                 } else {
01407                                         _pam_log(pamh, ctrl, LOG_NOTICE, "could not recover authentication token");
01408                                         retval = PAM_AUTHTOK_RECOVER_ERR;
01409                                 }
01410 
01411                         }
01412                         /*
01413                          * tidy up the conversation (resp_retcode) is ignored
01414                          * -- what is it for anyway? AGM
01415                          */
01416 
01417                         _pam_drop_reply(resp, i);
01418 
01419                 } else {
01420                         retval = (retval == PAM_SUCCESS)
01421                             ? PAM_AUTHTOK_RECOVER_ERR : retval;
01422                 }
01423         }
01424 
01425         if (retval != PAM_SUCCESS) {
01426                 _pam_log_debug(pamh, ctrl, LOG_DEBUG,
01427                                  "unable to obtain a password");
01428                 return retval;
01429         }
01430         /* 'token' is the entered password */
01431 
01432         /* we store this password as an item */
01433         
01434         retval = pam_set_item(pamh, authtok_flag, token);
01435         _pam_delete(token);     /* clean it up */
01436         if (retval != PAM_SUCCESS || 
01437             (retval = _pam_get_item(pamh, authtok_flag, &item)) != PAM_SUCCESS) {
01438                 
01439                 _pam_log(pamh, ctrl, LOG_CRIT, "error manipulating password");
01440                 return retval;
01441                 
01442         }
01443 
01444         *pass = item;
01445         item = NULL;            /* break link to password */
01446 
01447         return PAM_SUCCESS;
01448 }
01449 
01450 const char *get_conf_item_string(const pam_handle_t *pamh,
01451                                  int argc, 
01452                                  const char **argv, 
01453                                  int ctrl,
01454                                  dictionary *d,
01455                                  const char *item, 
01456                                  int config_flag)
01457 {
01458         int i = 0;
01459         const char *parm_opt = NULL;
01460         char *key = NULL;
01461 
01462         if (!(ctrl & config_flag)) {
01463                 goto out;
01464         }
01465 
01466         /* let the pam opt take precedence over the pam_winbind.conf option */
01467 
01468         if (d != NULL) {
01469 
01470                 if (!asprintf(&key, "global:%s", item)) {
01471                         goto out;
01472                 }
01473 
01474                 parm_opt = iniparser_getstr(d, key);
01475                 SAFE_FREE(key);
01476         }
01477 
01478         for ( i=0; i<argc; i++ ) {
01479 
01480                 if ((strncmp(argv[i], item, strlen(item)) == 0)) {
01481                         char *p;
01482 
01483                         if ( (p = strchr( argv[i], '=' )) == NULL) {
01484                                 _pam_log(pamh, ctrl, LOG_INFO, "no \"=\" delimiter for \"%s\" found\n", item);
01485                                 goto out;
01486                         }
01487                         _pam_log_debug(pamh, ctrl, LOG_INFO, "PAM config: %s '%s'\n", item, p+1);
01488                         return p + 1;
01489                 }
01490         }
01491 
01492         if (d != NULL) {
01493                 _pam_log_debug(pamh, ctrl, LOG_INFO, "CONFIG file: %s '%s'\n", item, parm_opt);
01494         }
01495 out:
01496         return parm_opt;
01497 }
01498 
01499 const char *get_krb5_cc_type_from_config(const pam_handle_t *pamh, int argc, const char **argv, int ctrl, dictionary *d)
01500 {
01501         return get_conf_item_string(pamh, argc, argv, ctrl, d, "krb5_ccache_type", WINBIND_KRB5_CCACHE_TYPE);
01502 }
01503 
01504 const char *get_member_from_config(const pam_handle_t *pamh, int argc, const char **argv, int ctrl, dictionary *d)
01505 {
01506         const char *ret = NULL;
01507         ret = get_conf_item_string(pamh, argc, argv, ctrl, d, "require_membership_of", WINBIND_REQUIRED_MEMBERSHIP);
01508         if (ret) {
01509                 return ret;
01510         }
01511         return get_conf_item_string(pamh, argc, argv, ctrl, d, "require-membership-of", WINBIND_REQUIRED_MEMBERSHIP);
01512 }
01513 
01514 PAM_EXTERN
01515 int pam_sm_authenticate(pam_handle_t *pamh, int flags,
01516                         int argc, const char **argv)
01517 {
01518         const char *username;
01519         const char *password;
01520         const char *member = NULL;
01521         const char *cctype = NULL;
01522         int retval = PAM_AUTH_ERR;
01523         dictionary *d = NULL;
01524         char *username_ret = NULL;
01525         char *new_authtok_required = NULL;
01526         char *real_username = NULL;
01527 
01528         /* parse arguments */
01529         int ctrl = _pam_parse(pamh, flags, argc, argv, &d);
01530         if (ctrl == -1) {
01531                 retval = PAM_SYSTEM_ERR;
01532                 goto out;
01533         }
01534 
01535         _PAM_LOG_FUNCTION_ENTER("pam_sm_authenticate", pamh, ctrl, flags);
01536 
01537         /* Get the username */
01538         retval = pam_get_user(pamh, &username, NULL);
01539         if ((retval != PAM_SUCCESS) || (!username)) {
01540                 _pam_log_debug(pamh, ctrl, LOG_DEBUG, "can not get the username");
01541                 retval = PAM_SERVICE_ERR;
01542                 goto out;
01543         }
01544 
01545 #if defined(AIX)
01546         /* Decode the user name since AIX does not support logn user
01547            names by default.  The name is encoded as _#uid.  */
01548 
01549         if ( username[0] == '_' ) {
01550                 uid_t id = atoi( &username[1] );
01551                 struct passwd *pw = NULL;               
01552 
01553                 if ( (id!=0) && ((pw = getpwuid( id )) != NULL) ) {
01554                         real_username = strdup( pw->pw_name );
01555                 }
01556         }
01557 #endif
01558 
01559         if ( !real_username ) {
01560                 /* Just making a copy of the username we got from PAM */
01561                 if ( (real_username = strdup( username )) == NULL ) {
01562                         _pam_log_debug(pamh, ctrl, LOG_DEBUG, 
01563                                        "memory allocation failure when copying username");
01564                         retval = PAM_SERVICE_ERR;
01565                         goto out;
01566                 }
01567         }       
01568 
01569         retval = _winbind_read_password(pamh, ctrl, NULL, 
01570                                         "Password: ", NULL,
01571                                         &password);
01572 
01573         if (retval != PAM_SUCCESS) {
01574                 _pam_log(pamh, ctrl, LOG_ERR, "Could not retrieve user's password");
01575                 retval = PAM_AUTHTOK_ERR;
01576                 goto out;
01577         }
01578 
01579         /* Let's not give too much away in the log file */
01580 
01581 #ifdef DEBUG_PASSWORD
01582         _pam_log_debug(pamh, ctrl, LOG_INFO, "Verify user '%s' with password '%s'", 
01583                        real_username, password);
01584 #else
01585         _pam_log_debug(pamh, ctrl, LOG_INFO, "Verify user '%s'", real_username);
01586 #endif
01587 
01588         member = get_member_from_config(pamh, argc, argv, ctrl, d);
01589 
01590         cctype = get_krb5_cc_type_from_config(pamh, argc, argv, ctrl, d);
01591 
01592         /* Now use the username to look up password */
01593         retval = winbind_auth_request(pamh, ctrl, username, password, member,
01594                                       cctype, NULL, NULL, &username_ret);
01595 
01596         if (retval == PAM_NEW_AUTHTOK_REQD ||
01597             retval == PAM_AUTHTOK_EXPIRED) {
01598 
01599                 char *new_authtok_required_during_auth = NULL;
01600 
01601                 if (!asprintf(&new_authtok_required, "%d", retval)) {
01602                         retval = PAM_BUF_ERR;
01603                         goto out;
01604                 }
01605 
01606                 pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, new_authtok_required, _pam_winbind_cleanup_func);
01607 
01608                 retval = PAM_SUCCESS;
01609 
01610                 if (!asprintf(&new_authtok_required_during_auth, "%d", True)) {
01611                         retval = PAM_BUF_ERR;
01612                         goto out;
01613                 }
01614 
01615                 pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH, 
01616                              new_authtok_required_during_auth, _pam_winbind_cleanup_func);
01617 
01618                 goto out;
01619         }
01620 
01621 out:
01622         if (username_ret) {
01623                 pam_set_item (pamh, PAM_USER, username_ret);
01624                 _pam_log_debug(pamh, ctrl, LOG_INFO, "Returned user was '%s'", username_ret);
01625                 free(username_ret);
01626         }
01627 
01628         if ( real_username ) {          
01629                 free( real_username );
01630         }       
01631                         
01632         if (d) {
01633                 iniparser_freedict(d);
01634         }
01635 
01636         if (!new_authtok_required) {
01637                 pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, NULL, NULL);
01638         }
01639 
01640         if (retval != PAM_SUCCESS) {
01641                 _pam_free_data_info3(pamh);
01642         }
01643 
01644         _PAM_LOG_FUNCTION_LEAVE("pam_sm_authenticate", pamh, ctrl, retval);
01645 
01646         return retval;
01647 }
01648 
01649 PAM_EXTERN
01650 int pam_sm_setcred(pam_handle_t *pamh, int flags,
01651                    int argc, const char **argv)
01652 {
01653         int ret = PAM_SYSTEM_ERR;
01654         dictionary *d = NULL;
01655 
01656         /* parse arguments */
01657         int ctrl = _pam_parse(pamh, flags, argc, argv, &d);
01658         if (ctrl == -1) {
01659                 ret = PAM_SYSTEM_ERR;
01660                 goto out;
01661         }
01662 
01663         _PAM_LOG_FUNCTION_ENTER("pam_sm_setcred", pamh, ctrl, flags);
01664 
01665         switch (flags & ~PAM_SILENT) {
01666 
01667                 case PAM_DELETE_CRED:
01668                         ret = pam_sm_close_session(pamh, flags, argc, argv);
01669                         break;
01670                 case PAM_REFRESH_CRED:
01671                         _pam_log_debug(pamh, ctrl, LOG_WARNING, "PAM_REFRESH_CRED not implemented");
01672                         ret = PAM_SUCCESS;
01673                         break;
01674                 case PAM_REINITIALIZE_CRED:
01675                         _pam_log_debug(pamh, ctrl, LOG_WARNING, "PAM_REINITIALIZE_CRED not implemented");
01676                         ret = PAM_SUCCESS;
01677                         break;
01678                 case PAM_ESTABLISH_CRED:
01679                         _pam_log_debug(pamh, ctrl, LOG_WARNING, "PAM_ESTABLISH_CRED not implemented");
01680                         ret = PAM_SUCCESS;
01681                         break;
01682                 default:
01683                         ret = PAM_SYSTEM_ERR;
01684                         break;
01685         }
01686 
01687  out:
01688         if (d) {
01689                 iniparser_freedict(d);
01690         }
01691 
01692         _PAM_LOG_FUNCTION_LEAVE("pam_sm_setcred", pamh, ctrl, ret);
01693         
01694         return ret;
01695 }
01696 
01697 /*
01698  * Account management. We want to verify that the account exists 
01699  * before returning PAM_SUCCESS
01700  */
01701 PAM_EXTERN
01702 int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
01703                    int argc, const char **argv)
01704 {
01705         const char *username;
01706         int ret = PAM_USER_UNKNOWN;
01707         void *tmp = NULL;
01708         dictionary *d = NULL;
01709 
01710         /* parse arguments */
01711         int ctrl = _pam_parse(pamh, flags, argc, argv, &d);
01712         if (ctrl == -1) {
01713                 return PAM_SYSTEM_ERR;
01714         }
01715 
01716         _PAM_LOG_FUNCTION_ENTER("pam_sm_acct_mgmt", pamh, ctrl, flags);
01717 
01718 
01719         /* Get the username */
01720         ret = pam_get_user(pamh, &username, NULL);
01721         if ((ret != PAM_SUCCESS) || (!username)) {
01722                 _pam_log_debug(pamh, ctrl, LOG_DEBUG,"can not get the username");
01723                 ret = PAM_SERVICE_ERR;
01724                 goto out;
01725         }
01726 
01727         /* Verify the username */
01728         ret = valid_user(pamh, ctrl, username);
01729         switch (ret) {
01730         case -1:
01731                 /* some sort of system error. The log was already printed */
01732                 ret = PAM_SERVICE_ERR;
01733                 goto out;
01734         case 1:
01735                 /* the user does not exist */
01736                 _pam_log_debug(pamh, ctrl, LOG_NOTICE, "user '%s' not found", username);
01737                 if (ctrl & WINBIND_UNKNOWN_OK_ARG) {
01738                         ret = PAM_IGNORE;
01739                         goto out;
01740                 }
01741                 ret = PAM_USER_UNKNOWN;
01742                 goto out;
01743         case 0:
01744                 pam_get_data( pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, (const void **)&tmp);
01745                 if (tmp != NULL) {
01746                         ret = atoi((const char *)tmp);
01747                         switch (ret) {
01748                         case PAM_AUTHTOK_EXPIRED:
01749                                 /* fall through, since new token is required in this case */
01750                         case PAM_NEW_AUTHTOK_REQD:
01751                                 _pam_log(pamh, ctrl, LOG_WARNING, "pam_sm_acct_mgmt success but %s is set", 
01752                                          PAM_WINBIND_NEW_AUTHTOK_REQD);
01753                                 _pam_log(pamh, ctrl, LOG_NOTICE, "user '%s' needs new password", username);
01754                                 /* PAM_AUTHTOKEN_REQD does not exist, but is documented in the manpage */
01755                                 ret = PAM_NEW_AUTHTOK_REQD;
01756                                 goto out;
01757                         default:
01758                                 _pam_log(pamh, ctrl, LOG_WARNING, "pam_sm_acct_mgmt success");
01759                                 _pam_log(pamh, ctrl, LOG_NOTICE, "user '%s' granted access", username);
01760                                 ret = PAM_SUCCESS;
01761                                 goto out;
01762                         }
01763                 }
01764 
01765                 /* Otherwise, the authentication looked good */
01766                 _pam_log(pamh, ctrl, LOG_NOTICE, "user '%s' granted access", username);
01767                 ret = PAM_SUCCESS;
01768                 goto out;
01769         default:
01770                 /* we don't know anything about this return value */
01771                 _pam_log(pamh, ctrl, LOG_ERR, "internal module error (ret = %d, user = '%s')", 
01772                          ret, username);
01773                 ret = PAM_SERVICE_ERR;
01774                 goto out;
01775         }
01776 
01777         /* should not be reached */
01778         ret = PAM_IGNORE;
01779 
01780  out:
01781 
01782         if (d) {
01783                 iniparser_freedict(d);
01784         }
01785 
01786         _PAM_LOG_FUNCTION_LEAVE("pam_sm_acct_mgmt", pamh, ctrl, ret);
01787         
01788         return ret;
01789 }
01790 
01791 PAM_EXTERN
01792 int pam_sm_open_session(pam_handle_t *pamh, int flags,
01793                         int argc, const char **argv)
01794 {
01795         int ret = PAM_SYSTEM_ERR;
01796         dictionary *d = NULL;
01797 
01798         /* parse arguments */
01799         int ctrl = _pam_parse(pamh, flags, argc, argv, &d);
01800         if (ctrl == -1) {
01801                 ret = PAM_SYSTEM_ERR;
01802                 goto out;
01803         }
01804 
01805         _PAM_LOG_FUNCTION_ENTER("pam_sm_open_session", pamh, ctrl, flags);
01806 
01807         ret = PAM_SUCCESS;
01808 
01809  out:
01810         if (d) {
01811                 iniparser_freedict(d);
01812         }
01813 
01814         _PAM_LOG_FUNCTION_LEAVE("pam_sm_open_session", pamh, ctrl, ret);
01815         
01816         return ret;
01817 }
01818 
01819 PAM_EXTERN
01820 int pam_sm_close_session(pam_handle_t *pamh, int flags,
01821                          int argc, const char **argv)
01822 {
01823         dictionary *d = NULL;
01824         int retval = PAM_SUCCESS;
01825 
01826         /* parse arguments */
01827         int ctrl = _pam_parse(pamh, flags, argc, argv, &d);
01828         if (ctrl == -1) {
01829                 retval = PAM_SYSTEM_ERR;
01830                 goto out;
01831         }
01832 
01833         _PAM_LOG_FUNCTION_ENTER("pam_sm_close_session", pamh, ctrl, flags);
01834 
01835         if (!(flags & PAM_DELETE_CRED)) {
01836                 retval = PAM_SUCCESS;
01837                 goto out;
01838         }
01839 
01840         if (ctrl & WINBIND_KRB5_AUTH) {
01841 
01842                 /* destroy the ccache here */
01843                 struct winbindd_request request;
01844                 struct winbindd_response response;
01845                 const char *user;
01846                 const char *ccname = NULL;
01847                 struct passwd *pwd = NULL;
01848 
01849                 ZERO_STRUCT(request);
01850                 ZERO_STRUCT(response);
01851 
01852                 retval = pam_get_user(pamh, &user, "Username: ");
01853                 if (retval) {
01854                         _pam_log(pamh, ctrl, LOG_ERR, "could not identify user");
01855                         goto out;
01856                 }
01857 
01858                 if (user == NULL) {
01859                         _pam_log(pamh, ctrl, LOG_ERR, "username was NULL!");
01860                         retval = PAM_USER_UNKNOWN;
01861                         goto out;
01862                 }
01863 
01864                 _pam_log_debug(pamh, ctrl, LOG_DEBUG, "username [%s] obtained", user);
01865 
01866                 ccname = pam_getenv(pamh, "KRB5CCNAME");
01867                 if (ccname == NULL) {
01868                         _pam_log_debug(pamh, ctrl, LOG_DEBUG, "user has no KRB5CCNAME environment");
01869                 }
01870 
01871                 strncpy(request.data.logoff.user, user,
01872                         sizeof(request.data.logoff.user) - 1);
01873 
01874                 if (ccname) {
01875                         strncpy(request.data.logoff.krb5ccname, ccname,
01876                                 sizeof(request.data.logoff.krb5ccname) - 1);
01877                 }
01878 
01879                 pwd = getpwnam(user);
01880                 if (pwd == NULL) {
01881                         retval = PAM_USER_UNKNOWN;
01882                         goto out;
01883                 }
01884                 request.data.logoff.uid = pwd->pw_uid;
01885 
01886                 request.flags = WBFLAG_PAM_KRB5 | WBFLAG_PAM_CONTACT_TRUSTDOM;
01887 
01888                 retval = pam_winbind_request_log(pamh, ctrl, WINBINDD_PAM_LOGOFF, &request, &response, user);
01889         }
01890 
01891 out:
01892         if (d) {
01893                 iniparser_freedict(d);
01894         }
01895 
01896         _PAM_LOG_FUNCTION_LEAVE("pam_sm_close_session", pamh, ctrl, retval);
01897         
01898         return retval;
01899 }
01900 
01901 /**
01902  * evaluate whether we need to re-authenticate with kerberos after a password change
01903  * 
01904  * @param pamh PAM handle
01905  * @param ctrl PAM winbind options.
01906  * @param user The username
01907  *
01908  * @return boolean Returns True if required, False if not.
01909  */
01910 
01911 static BOOL _pam_require_krb5_auth_after_chauthtok(pam_handle_t *pamh, int ctrl, const char *user)
01912 {
01913 
01914         /* Make sure that we only do this if
01915          * a) the chauthtok got initiated during a logon attempt (authenticate->acct_mgmt->chauthtok)
01916          * b) any later password change via the "passwd" command if done by the user itself 
01917          *
01918          * NB. If we login from gdm or xdm and the password expires,
01919          * we change the password, but there is no memory cache.
01920          * Thus, even for passthrough login, we should do the
01921          * authentication again to update memory cache.
01922          * --- BoYang
01923          * */
01924 
01925         char *new_authtok_reqd_during_auth = NULL;
01926         struct passwd *pwd = NULL;
01927 
01928         _pam_get_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH, &new_authtok_reqd_during_auth);
01929         pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH, NULL, NULL);
01930 
01931         if (new_authtok_reqd_during_auth) {
01932                 return True;
01933         }
01934 
01935         pwd = getpwnam(user);
01936         if (!pwd) {
01937                 return False;
01938         }
01939 
01940         if (getuid() == pwd->pw_uid) {
01941                 return True;
01942         }
01943 
01944         return False;
01945 }
01946 
01947 
01948 PAM_EXTERN 
01949 int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
01950                      int argc, const char **argv)
01951 {
01952         unsigned int lctrl;
01953         int ret;
01954         unsigned int ctrl;
01955         bool cached_login = False;
01956 
01957         /* <DO NOT free() THESE> */
01958         const char *user;
01959         char *pass_old, *pass_new;
01960         /* </DO NOT free() THESE> */
01961 
01962         char *Announce;
01963         
01964         int retry = 0;
01965         dictionary *d = NULL;
01966         char *username_ret = NULL;
01967         struct winbindd_response response;
01968 
01969         ZERO_STRUCT(response);
01970 
01971         ctrl = _pam_parse(pamh, flags, argc, argv, &d);
01972         if (ctrl == -1) {
01973                 ret = PAM_SYSTEM_ERR;
01974                 goto out;
01975         }
01976 
01977         _PAM_LOG_FUNCTION_ENTER("pam_sm_chauthtok", pamh, ctrl, flags);
01978 
01979         cached_login = (ctrl & WINBIND_CACHED_LOGIN);
01980 
01981         /* clearing offline bit for auth */
01982         ctrl &= ~WINBIND_CACHED_LOGIN;
01983 
01984         /*
01985          * First get the name of a user
01986          */
01987         ret = pam_get_user(pamh, &user, "Username: ");
01988         if (ret) {
01989                 _pam_log(pamh, ctrl, LOG_ERR,
01990                          "password - could not identify user");
01991                 goto out;
01992         }
01993 
01994         if (user == NULL) {
01995                 _pam_log(pamh, ctrl, LOG_ERR, "username was NULL!");
01996                 ret = PAM_USER_UNKNOWN;
01997                 goto out;
01998         }
01999 
02000         _pam_log_debug(pamh, ctrl, LOG_DEBUG, "username [%s] obtained", user);
02001 
02002         /* check if this is really a user in winbindd, not only in NSS */
02003         ret = valid_user(pamh, ctrl, user);
02004         switch (ret) {
02005                 case 1:
02006                         ret = PAM_USER_UNKNOWN;
02007                         goto out;
02008                 case -1:
02009                         ret = PAM_SYSTEM_ERR;
02010                         goto out;
02011                 default:
02012                         break;
02013         }
02014                 
02015         /*
02016          * obtain and verify the current password (OLDAUTHTOK) for
02017          * the user.
02018          */
02019 
02020         if (flags & PAM_PRELIM_CHECK) {
02021                 time_t pwdlastset_prelim = 0;
02022                 
02023                 /* instruct user what is happening */
02024 #define greeting "Changing password for "
02025                 Announce = (char *) malloc(sizeof(greeting) + strlen(user));
02026                 if (Announce == NULL) {
02027                         _pam_log(pamh, ctrl, LOG_CRIT, "password - out of memory");
02028                         ret = PAM_BUF_ERR;
02029                         goto out;
02030                 }
02031                 (void) strcpy(Announce, greeting);
02032                 (void) strcpy(Announce + sizeof(greeting) - 1, user);
02033 #undef greeting
02034                 
02035                 lctrl = ctrl | WINBIND__OLD_PASSWORD;
02036                 ret = _winbind_read_password(pamh, lctrl,
02037                                                 Announce,
02038                                                 "(current) NT password: ",
02039                                                 NULL,
02040                                                 (const char **) &pass_old);
02041                 if (ret != PAM_SUCCESS) {
02042                         _pam_log(pamh, ctrl, LOG_NOTICE, "password - (old) token not obtained");
02043                         goto out;
02044                 }
02045 
02046                 /* verify that this is the password for this user */
02047                 
02048                 ret = winbind_auth_request(pamh, ctrl, user, pass_old,
02049                                         NULL, NULL, &response, &pwdlastset_prelim, NULL);
02050 
02051                 if (ret != PAM_ACCT_EXPIRED && 
02052                     ret != PAM_AUTHTOK_EXPIRED &&
02053                     ret != PAM_NEW_AUTHTOK_REQD &&
02054                     ret != PAM_SUCCESS) {
02055                         pass_old = NULL;
02056                         goto out;
02057                 }
02058                 
02059                 pam_set_data(pamh, PAM_WINBIND_PWD_LAST_SET, (void *)pwdlastset_prelim, NULL);
02060 
02061                 ret = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
02062                 pass_old = NULL;
02063                 if (ret != PAM_SUCCESS) {
02064                         _pam_log(pamh, ctrl, LOG_CRIT, "failed to set PAM_OLDAUTHTOK");
02065                 }
02066         } else if (flags & PAM_UPDATE_AUTHTOK) {
02067         
02068                 time_t pwdlastset_update = 0;
02069                 
02070                 /*
02071                  * obtain the proposed password
02072                  */
02073                 
02074                 /*
02075                  * get the old token back. 
02076                  */
02077                 
02078                 ret = _pam_get_item(pamh, PAM_OLDAUTHTOK, &pass_old);
02079                 
02080                 if (ret != PAM_SUCCESS) {
02081                         _pam_log(pamh, ctrl, LOG_NOTICE, "user not authenticated");
02082                         goto out;
02083                 }
02084                 
02085                 lctrl = ctrl & ~WINBIND_TRY_FIRST_PASS_ARG;
02086                 
02087                 if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
02088                         lctrl |= WINBIND_USE_FIRST_PASS_ARG;
02089                 }
02090                 retry = 0;
02091                 ret = PAM_AUTHTOK_ERR;
02092                 while ((ret != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
02093                         /*
02094                          * use_authtok is to force the use of a previously entered
02095                          * password -- needed for pluggable password strength checking
02096                          */
02097                         
02098                         ret = _winbind_read_password(pamh, lctrl,
02099                                                         NULL,
02100                                                         "Enter new NT password: ",
02101                                                         "Retype new NT password: ",
02102                                                         (const char **) &pass_new);
02103                         
02104                         if (ret != PAM_SUCCESS) {
02105                                 _pam_log_debug(pamh, ctrl, LOG_ALERT
02106                                          ,"password - new password not obtained");
02107                                 pass_old = NULL;/* tidy up */
02108                                 goto out;
02109                         }
02110 
02111                         /*
02112                          * At this point we know who the user is and what they
02113                          * propose as their new password. Verify that the new
02114                          * password is acceptable.
02115                          */
02116                         
02117                         if (pass_new[0] == '\0') {/* "\0" password = NULL */
02118                                 pass_new = NULL;
02119                         }
02120                 }
02121                 
02122                 /*
02123                  * By reaching here we have approved the passwords and must now
02124                  * rebuild the password database file.
02125                  */
02126                 _pam_get_data( pamh, PAM_WINBIND_PWD_LAST_SET,
02127                                &pwdlastset_update);
02128 
02129                 /*
02130                  * if cached creds were enabled, make sure to set the
02131                  * WINBIND_CACHED_LOGIN bit here in order to have winbindd
02132                  * update the cached creds storage - gd
02133                  */
02134                 if (cached_login) {
02135                         ctrl |= WINBIND_CACHED_LOGIN;
02136                 }
02137 
02138                 ret = winbind_chauthtok_request(pamh, ctrl, user, pass_old, pass_new, pwdlastset_update);
02139                 if (ret) {
02140                         _pam_overwrite(pass_new);
02141                         _pam_overwrite(pass_old);
02142                         pass_old = pass_new = NULL;
02143                         goto out;
02144                 }
02145 
02146                 if (_pam_require_krb5_auth_after_chauthtok(pamh, ctrl, user)) {
02147 
02148                         const char *member = get_member_from_config(pamh, argc, argv, ctrl, d);
02149                         const char *cctype = get_krb5_cc_type_from_config(pamh, argc, argv, ctrl, d);
02150 
02151                         /* Keep the WINBIND_CACHED_LOGIN bit for
02152                          * authentication after changing the password.
02153                          * This will update the cached credentials in case
02154                          * that winbindd_dual_pam_chauthtok() fails
02155                          * to update them.
02156                          * --- BoYang
02157                          * */
02158 
02159                         ret = winbind_auth_request(pamh, ctrl, user, pass_new,
02160                                                         member, cctype, &response, NULL, &username_ret);
02161                         _pam_overwrite(pass_new);
02162                         _pam_overwrite(pass_old);
02163                         pass_old = pass_new = NULL;
02164 
02165                         if (ret == PAM_SUCCESS) {
02166                         
02167                                 /* warn a user if the password is about to expire soon */
02168                                 _pam_warn_password_expiry(pamh, ctrl, &response, NULL);
02169 
02170                                 /* set some info3 info for other modules in the stack */
02171                                 _pam_set_data_info3(pamh, ctrl, &response);
02172 
02173                                 /* put krb5ccname into env */
02174                                 _pam_setup_krb5_env(pamh, ctrl, response.data.auth.krb5ccname);
02175 
02176                                 if (username_ret) {
02177                                         pam_set_item (pamh, PAM_USER, username_ret);
02178                                         _pam_log_debug(pamh, ctrl, LOG_INFO, "Returned user was '%s'", username_ret);
02179                                         free(username_ret);
02180                                 }
02181                         }
02182 
02183                         goto out;
02184                 }
02185         } else {
02186                 ret = PAM_SERVICE_ERR;
02187         }
02188 
02189 out:
02190         if (d) {
02191                 iniparser_freedict(d);
02192         }
02193 
02194         /* Deal with offline errors. */
02195         PAM_WB_REMARK_CHECK_RESPONSE(pamh, ctrl, response, "NT_STATUS_NO_LOGON_SERVERS");
02196         PAM_WB_REMARK_CHECK_RESPONSE(pamh, ctrl, response, "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND");
02197         PAM_WB_REMARK_CHECK_RESPONSE(pamh, ctrl, response, "NT_STATUS_ACCESS_DENIED");
02198 
02199         _PAM_LOG_FUNCTION_LEAVE("pam_sm_chauthtok", pamh, ctrl, ret);
02200         
02201         return ret;
02202 }
02203 
02204 #ifdef PAM_STATIC
02205 
02206 /* static module data */
02207 
02208 struct pam_module _pam_winbind_modstruct = {
02209         MODULE_NAME,
02210         pam_sm_authenticate,
02211         pam_sm_setcred,
02212         pam_sm_acct_mgmt,
02213         pam_sm_open_session,
02214         pam_sm_close_session,
02215         pam_sm_chauthtok
02216 };
02217 
02218 #endif
02219 
02220 /*
02221  * Copyright (c) Andrew Tridgell  <tridge@samba.org>   2000
02222  * Copyright (c) Tim Potter       <tpot@samba.org>     2000
02223  * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
02224  * Copyright (c) Guenther Deschner <gd@samba.org>      2005-2007
02225  * Copyright (c) Jan R棚korajski 1999.
02226  * Copyright (c) Andrew G. Morgan 1996-8.
02227  * Copyright (c) Alex O. Yuriev, 1996.
02228  * Copyright (c) Cristian Gafton 1996.
02229  * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software. 
02230  *
02231  * Redistribution and use in source and binary forms, with or without
02232  * modification, are permitted provided that the following conditions
02233  * are met:
02234  * 1. Redistributions of source code must retain the above copyright
02235  *    notice, and the entire permission notice in its entirety,
02236  *    including the disclaimer of warranties.
02237  * 2. Redistributions in binary form must reproduce the above copyright
02238  *    notice, this list of conditions and the following disclaimer in the
02239  *    documentation and/or other materials provided with the distribution.
02240  * 3. The name of the author may not be used to endorse or promote
02241  *    products derived from this software without specific prior
02242  *    written permission.
02243  *
02244  * ALTERNATIVELY, this product may be distributed under the terms of
02245  * the GNU Public License, in which case the provisions of the GPL are
02246  * required INSTEAD OF the above restrictions.  (This clause is
02247  * necessary due to a potential bad interaction between the GPL and
02248  * the restrictions contained in a BSD-style copyright.)
02249  *
02250  * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
02251  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
02252  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
02253  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
02254  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
02255  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
02256  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
02257  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
02258  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
02259  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
02260  * OF THE POSSIBILITY OF SUCH DAMAGE.
02261  */

Sambaに対してSat Aug 29 21:23:09 2009に生成されました。  doxygen 1.4.7