Please note that in the examples of PAM src the PAM_CHANGE_EXPIRED_AUTHOK flag is always used after the check about the expired password (PAM_NEW_AUTHTOK_REQD): pam-1.1.3$ grep -n4 -r PAM_CHANGE_EXPIRED_AUTHTOK examples/ examples/xsh.c-105- bail_out(pamh,0,retcode,"pam_acct_mgmt"); examples/xsh.c-106- examples/xsh.c-107- if (retcode == PAM_NEW_AUTHTOK_REQD) { examples/xsh.c-108- fprintf(stderr,"Application must request new password...\n"); examples/xsh.c:109: retcode = pam_chauthtok(pamh,PAM_CHANGE_EXPIRED_AUTHTOK); examples/xsh.c-110- bail_out(pamh,0,retcode,"pam_chauthtok"); examples/xsh.c-111- } examples/xsh.c-112- examples/xsh.c-113- if (retcode != PAM_SUCCESS) { -- examples/blank.c-101- bail_out(pamh,0,retcode,"pam_acct_mgmt"); examples/blank.c-102- examples/blank.c-103- if (retcode == PAM_NEW_AUTHTOK_REQD) { examples/blank.c-104- fprintf(stderr,"Application must request new password...\n"); examples/blank.c:105: retcode = pam_chauthtok(pamh,PAM_CHANGE_EXPIRED_AUTHTOK); examples/blank.c-106- bail_out(pamh,0,retcode,"pam_chauthtok"); examples/blank.c-107- } examples/blank.c-108- examples/blank.c-109- if (retcode != PAM_SUCCESS) { The same happens with gdm3... gdm3-3.4.1$ grep -n4 -r PAM_CHANGE_EXPIRED_AUTHTOK * daemon/gdm-session-worker.c-1287- daemon/gdm-session-worker.c-1288- /* it's possible that the user needs to change their password or pin code daemon/gdm-session-worker.c-1289- */ daemon/gdm-session-worker.c-1290- if (error_code == PAM_NEW_AUTHTOK_REQD && !worker->priv->is_program_session) { daemon/gdm-session-worker.c:1291: error_code = pam_chauthtok (worker->priv->pam_handle, PAM_CHANGE_EXPIRED_AUTHTOK); daemon/gdm-session-worker.c-1292- daemon/gdm-session-worker.c-1293- gdm_session_worker_get_username (worker, NULL); daemon/gdm-session-worker.c-1294- daemon/gdm-session-worker.c-1295- if (error_code != PAM_SUCCESS) { ...and passwd: shadow-4.1.5.1$ grep -n4 -r PAM_CHANGE_EXPIRED_AUTHTOK * libmisc/pam_pass.c-54- libmisc/pam_pass.c-55- if (silent) libmisc/pam_pass.c-56- flags |= PAM_SILENT; libmisc/pam_pass.c-57- if (change_expired) libmisc/pam_pass.c:58: flags |= PAM_CHANGE_EXPIRED_AUTHTOK; libmisc/pam_pass.c-59- libmisc/pam_pass.c-60- ret = pam_start ("passwd", user, &conv, &pamh); libmisc/pam_pass.c-61- if (ret != PAM_SUCCESS) { libmisc/pam_pass.c-62- fprintf (stderr, -- src/su.c-436- fprintf (stderr, src/su.c-437- _("%s: %s\n(Ignored)\n"), src/su.c-438- Prog, pam_strerror (pamh, ret)); src/su.c-439- } else if (PAM_NEW_AUTHTOK_REQD == ret) { src/su.c:440: ret = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK); src/su.c-441- if (PAM_SUCCESS != ret) { src/su.c-442- SYSLOG ((LOG_ERR, "pam_chauthtok: %s", src/su.c-443- pam_strerror (pamh, ret))); src/su.c-444- fprintf (stderr, -- src/login.c-857- src/login.c-858- /* Check the account validity */ src/login.c-859- retcode = pam_acct_mgmt (pamh, 0); src/login.c-860- if (retcode == PAM_NEW_AUTHTOK_REQD) { src/login.c:861: retcode = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK); src/login.c-862- } src/login.c-863- PAM_FAIL_CHECK; src/login.c-864- src/login.c-865- /* Open the PAM session */