Skip to content

Commit 0d24169

Browse files
committed
Manually merge PR208 through svn as I should have done in the first place.
git-svn-id: svn+ssh://svn.code.sf.net/p/migrid/code/trunk@6212 b75ad72c-e7d7-11dd-a971-7dbc132099af
1 parent 5f71668 commit 0d24169

File tree

2 files changed

+75
-23
lines changed

2 files changed

+75
-23
lines changed

mig/src/libpam-mig/libpam_mig.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* libpam_mig - PAM module for MiG user authentication
3-
* Copyright (C) 2003-2024 The MiG Project lead by Brian Vinter
3+
* Copyright (C) 2003-2025 The MiG Project lead by the Science HPC Center at UCPH
44
*
55
* This file is part of MiG
66
*
@@ -418,7 +418,7 @@ int pam_sm_authenticate_exit(int exit_value, struct pam_response *pwresp)
418418
/* Free password response struct */
419419
free_pam_response(pwresp, 1);
420420
#ifdef ENABLE_AUTHHANDLER
421-
if (false == mig_pyexit()) {
421+
if (false == mig_pyexit(exit_value)) {
422422
result = PAM_AUTH_ERR;
423423
}
424424
#endif /* ENABLE_AUTHHANDLER */
@@ -448,8 +448,12 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags,
448448
struct pam_response *pwresp = NULL;
449449

450450
WRITELOGMESSAGE(LOG_DEBUG,
451-
"In pam_sm_authenticate: pamh=%p flags=%x, argc=%d, argv:%p\n",
452-
(void *)pamh, flags, argc, (void *)argv);
451+
"In pam_sm_authenticate: pamh=%p flags=%x, argc=%d, argv:%p, migauth_exit:%d\n",
452+
(void *)pamh, flags, argc, (void *)argv, migauth_exit);
453+
if (migauth_exit == true) {
454+
WRITELOGMESSAGE(LOG_DEBUG, "migauthhandler blocked further password attempts\n");
455+
return pam_sm_authenticate_exit(PAM_AUTH_ERR, pwresp);
456+
}
453457

454458
retval = pam_sm_authenticate_init();
455459
if (retval != PAM_SUCCESS) {

mig/src/libpam-mig/migauthhandler.c

Lines changed: 67 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* migauthhandler.c - C <-> Python wrappers for MiG user authentication
3-
* Copyright (C) 2003-2023 The MiG Project lead by Brian Vinter
3+
* Copyright (C) 2003-2025 The MiG Project lead by the Science HPC Center at UCPH
44
*
55
* This file is part of MiG
66
*
@@ -84,7 +84,14 @@
8484
#define RATE_LIMIT_EXPIRE_DELAY 120
8585
#endif
8686

87+
#ifndef MAX_AUTH_TRIES
88+
#define MAX_AUTH_TRIES 3
89+
#endif
90+
8791
void *libpython_handle = NULL;
92+
unsigned int migauth_tries = 0;
93+
bool migauth_exit = false;
94+
8895
PyObject *py_main = NULL;
8996

9097
static void pyrun(const char *cmd, ...)
@@ -110,16 +117,19 @@ static bool mig_pyinit()
110117
{
111118
// https://stackoverflow.com/questions/11842920/undefined-symbol-pyexc-importerror-when-embedding-python-in-c/50489814#50489814
112119
if (libpython_handle != NULL) {
113-
WRITELOGMESSAGE(LOG_DEBUG, "Python already initialized\n");
120+
migauth_tries += 1;
121+
WRITELOGMESSAGE(LOG_DEBUG,
122+
"Python already initialized with migauth_tries: %d/%d\n",
123+
migauth_tries, MAX_AUTH_TRIES);
114124
} else {
125+
migauth_tries = 1;
115126
// NOTE: use make-detected LIBPYTHON shared library and RTLD_NOW
116127
// NOTE: The issue with RTLD_LAZY is that C-extensions do not have dependency on the libpython
117128
// (as can be seen with help of ldd), so once they are loaded and a symbol (e.g. PyFloat_Type)
118129
// from libpython which is not yet resolved must be looked up,
119130
// the dynamic linker doesn't know that it has to look into the libpython.
120131
// https://stackoverflow.com/questions/67891197/ctypes-cpython-39-x86-64-linux-gnu-so-undefined-symbol-pyfloat-type-in-embedd
121132
libpython_handle = dlopen(LIBPYTHON, RTLD_NOW | RTLD_GLOBAL);
122-
123133
#if PY_VERSION_HEX < 0x03000000
124134
Py_SetProgramName("pam-mig");
125135
#else
@@ -136,6 +146,9 @@ static bool mig_pyinit()
136146
WRITELOGMESSAGE(LOG_ERR, "Failed to find Python __main__\n");
137147
return false;
138148
}
149+
WRITELOGMESSAGE(LOG_DEBUG,
150+
"Python initialized with migauth_tries: %d/%d\n",
151+
migauth_tries, MAX_AUTH_TRIES);
139152
pyrun("from __future__ import absolute_import");
140153

141154
pyrun("import os");
@@ -164,14 +177,24 @@ static bool mig_pyinit()
164177
return true;
165178
}
166179

167-
static bool mig_pyexit()
180+
static bool mig_pyexit(int exit_value)
168181
{
169182
if (libpython_handle == NULL) {
170183
WRITELOGMESSAGE(LOG_DEBUG, "Python already finalized\n");
171-
} else {
184+
} else if (exit_value == PAM_SUCCESS \
185+
|| migauth_exit == true \
186+
|| migauth_tries >= MAX_AUTH_TRIES) {
187+
WRITELOGMESSAGE(LOG_DEBUG,
188+
"Python finalize with exit value: %d, migauth_exit: %d, migauth_tries: %d/%d\n",
189+
exit_value, migauth_exit, migauth_tries, MAX_AUTH_TRIES);
172190
Py_Finalize();
173191
dlclose(libpython_handle);
174192
libpython_handle = NULL;
193+
migauth_exit = true;
194+
} else {
195+
WRITELOGMESSAGE(LOG_DEBUG,
196+
"mig_pyexit called with exit_value: %d migauth_tries: %d/%d\n",
197+
exit_value, migauth_tries, MAX_AUTH_TRIES);
175198
}
176199
return true;
177200
}
@@ -331,20 +354,26 @@ static bool mig_reg_auth_attempt(const unsigned int mode,
331354
const char *address, const char *secret)
332355
{
333356
bool result = false;
357+
bool disconnect = true;
334358
WRITELOGMESSAGE(LOG_DEBUG,
335359
"mode: 0x%X, username: %s, address: %s, secret: %s\n",
336360
mode, username, address, secret);
337-
char pycmd[MAX_PYCMD_LENGTH] =
338-
"(authorized, disconnect) = validate_auth_attempt(configuration, 'sftp-subsys', ";
339-
char pytmp[MAX_PYCMD_LENGTH];
361+
/* Filter valid auth types first - we currently only allow password auth */
340362
if (mode & MIG_AUTHTYPE_PASSWORD) {
341-
strncat(&pycmd[0], "'password', ", MAX_PYCMD_LENGTH - strlen(pycmd));
363+
WRITELOGMESSAGE(LOG_DEBUG, "proceed with password authentication\n");
342364
} else {
343365
WRITELOGMESSAGE(LOG_ERR,
344366
"mig_reg_auth_attempt: No valid auth-type in mode: 0x%X\n",
345367
mode);
368+
/* We don't exit hard here to make sure other auth types may follow */
346369
return false;
347370
}
371+
char pycmd[MAX_PYCMD_LENGTH] =
372+
"(authorized, disconnect) = validate_auth_attempt(configuration, 'sftp-subsys', ";
373+
char pytmp[MAX_PYCMD_LENGTH];
374+
/* Always password auth here as mentioned in the above comment */
375+
strncat(&pycmd[0], "'password', ", MAX_PYCMD_LENGTH - strlen(pycmd));
376+
348377
strncat(&pycmd[0], "'", MAX_PYCMD_LENGTH - strlen(pycmd));
349378
strncat(&pycmd[0], username, MAX_PYCMD_LENGTH - strlen(pycmd));
350379
strncat(&pycmd[0], "', '", MAX_PYCMD_LENGTH - strlen(pycmd));
@@ -415,17 +444,36 @@ static bool mig_reg_auth_attempt(const unsigned int mode,
415444
MAX_PYCMD_LENGTH - strlen(pycmd));
416445
}
417446
strncat(&pycmd[0], ")", MAX_PYCMD_LENGTH - strlen(pycmd));
418-
if (MAX_PYCMD_LENGTH == strlen(pycmd)) {
419-
WRITELOGMESSAGE(LOG_ERR, "mig_reg_auth_attempt: pycmd overflow\n");
420-
return false;
421-
}
422-
pyrun(&pycmd[0]);
423-
PyObject *py_authorized = PyObject_GetAttrString(py_main, "authorized");
424-
if (py_authorized == NULL) {
425-
WRITELOGMESSAGE(LOG_ERR, "Missing python variable: py_authorized\n");
447+
/* Execute python command if and only if it didn't overflow */
448+
if (MAX_PYCMD_LENGTH > strlen(pycmd)) {
449+
pyrun(&pycmd[0]);
450+
PyObject *py_authorized = PyObject_GetAttrString(py_main, "authorized");
451+
if (py_authorized == NULL) {
452+
WRITELOGMESSAGE(LOG_ERR, "Missing python variable: py_authorized\n");
453+
} else {
454+
result = PyObject_IsTrue(py_authorized);
455+
Py_DECREF(py_authorized);
456+
}
457+
PyObject *py_disconnect = PyObject_GetAttrString(py_main, "disconnect");
458+
if (py_disconnect == NULL) {
459+
WRITELOGMESSAGE(LOG_ERR, "Missing python variable: py_disconnect\n");
460+
} else {
461+
disconnect = PyObject_IsTrue(py_disconnect);
462+
Py_DECREF(py_disconnect);
463+
}
426464
} else {
427-
result = PyObject_IsTrue(py_authorized);
428-
Py_DECREF(py_authorized);
465+
WRITELOGMESSAGE(LOG_ERR, "mig_reg_auth_attempt: pycmd overflow!\n");
466+
}
467+
468+
/* NOTE: '(mode & MIG_VALID_AUTH)'
469+
If caller (libpam_mig) validated credentials
470+
then there are no more passwords (re-)tries.
471+
We honor 'disconnect' here to follow central password policy.
472+
*/
473+
if (disconnect == true || (mode & MIG_VALID_AUTH)) {
474+
WRITELOGMESSAGE(LOG_DEBUG, "disconnect: %d, mode & MIG_VALID_AUTH: %d\n",
475+
disconnect, (mode & MIG_VALID_AUTH));
476+
migauth_exit = true;
429477
}
430478

431479
return result;

0 commit comments

Comments
 (0)