Skip to content

Commit f21db9c

Browse files
committed
Adjust snprintf overflow checks for increased robustness as suggested by @rasmunk . Reworked the get runtime int variable helper to support all possible (long long) integers we may potentially encounter while adding a similar snprintf check there.
1 parent e28801a commit f21db9c

File tree

3 files changed

+51
-14
lines changed

3 files changed

+51
-14
lines changed

mig/src/include/auth/migauth.h

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* migauth.h - PAM and NSS helpers 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
*
@@ -84,6 +84,14 @@
8484
/* Default fall-back value used unless given */
8585
#define PASSWORD_MIN_CLASSES 2
8686
#endif
87+
#ifndef INTEGER_MAX_LENGTH
88+
/* Default fall-back value used unless given */
89+
/* This size is based on long long integer size i.e. LLONG_MAX in C and
90+
* sys.maxsize in python, which should always fit in at most 21 bytes even
91+
* when optional sign ('-') and the terminating null-byte is included.
92+
*/
93+
#define INTEGER_MAX_LENGTH 21
94+
#endif
8795

8896
/* Various settings used by ordinary user access */
8997
#ifndef USER_HOME
@@ -257,13 +265,26 @@ static const int get_runtime_var_int(const char *env_name,
257265
const int define_val)
258266
{
259267
/* NOTE: tedious but required juggling between string and integer */
260-
char val_str[4];
268+
269+
/* IMPORTANT: when constructing strings from unknown input we must use
270+
* snprintf with the actual buffer size as max size parameter and check
271+
* that the returned value was smaller than that size. Any bigger return
272+
* value means that the buffer was written but the input truncated when
273+
* it attempted to write the returned number of bytes.
274+
* https://pubs.opengroup.org/onlinepubs/9799919799/functions/snprintf.html
275+
*/
276+
277+
char val_str[INTEGER_MAX_LENGTH];
261278
/* Convert define_val int to '\0'-terminated string */
262-
snprintf(val_str, 3, "%d", define_val);
263-
val_str[3] = 0;
279+
memset(val_str, 0, INTEGER_MAX_LENGTH);
280+
if (INTEGER_MAX_LENGTH <=
281+
snprintf(val_str, INTEGER_MAX_LENGTH, "%d", define_val)) {
282+
WRITELOGMESSAGE(LOG_ERR, "Overflow in get runtime int var %s: %s\n",
283+
env_name, define_val);
284+
return -42;
285+
}
264286
/* Convert lookup back to int */
265287
return atoi(get_runtime_var(env_name, conf_name, val_str));
266-
267288
}
268289

269290
static const int get_runtime_var_size_t(const char *env_name,

mig/src/libnss-mig/libnss_mig.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* --- BEGIN_HEADER ---
33
*
44
* libnss_mig - NSS module for MiG user authentication
5-
* Copyright (C) 2003-2022 The MiG Project lead by Brian Vinter
5+
* Copyright (C) 2003-2025 The MiG Project lead by the Science HPC Center at UCPH
66
*
77
* This file is part of MiG
88
*
@@ -174,6 +174,14 @@ _nss_mig_getpwnam_r(const char *name,
174174
*/
175175
int keep_trying = 1;
176176

177+
/* IMPORTANT: when constructing strings from unknown input we must use
178+
* snprintf with the actual buffer size as max size parameter and check
179+
* that the returned value was smaller than that size. Any bigger return
180+
* value means that the buffer was written but the input truncated when
181+
* it attempted to write the returned number of bytes.
182+
* https://pubs.opengroup.org/onlinepubs/9799919799/functions/snprintf.html
183+
*/
184+
177185
#ifdef ENABLE_SHARELINK
178186
/* Optional anonymous share link access:
179187
- username must have fixed length matching get_sharelink_length()
@@ -183,7 +191,7 @@ _nss_mig_getpwnam_r(const char *name,
183191
if (keep_trying && name_len == get_sharelink_length()) {
184192
WRITELOGMESSAGE(LOG_DEBUG, "Checking for sharelink: %s\n", name);
185193
memset(pathbuf, 0, PATH_BUF_LEN);
186-
if (PATH_BUF_LEN ==
194+
if (PATH_BUF_LEN <=
187195
snprintf(pathbuf, PATH_BUF_LEN, "%s/%s/%s",
188196
get_sharelink_home(), SHARELINK_SUBDIR, name)) {
189197
WRITELOGMESSAGE(LOG_WARNING,
@@ -237,7 +245,7 @@ _nss_mig_getpwnam_r(const char *name,
237245
if (keep_trying && name_len == get_jobsidmount_length()) {
238246
WRITELOGMESSAGE(LOG_DEBUG, "Checking for jobsidmount: %s\n", name);
239247
memset(pathbuf, 0, PATH_BUF_LEN);
240-
if (PATH_BUF_LEN ==
248+
if (PATH_BUF_LEN <=
241249
snprintf(pathbuf, PATH_BUF_LEN, "%s/%s",
242250
get_jobsidmount_home(), name)) {
243251
WRITELOGMESSAGE(LOG_WARNING,
@@ -290,7 +298,7 @@ _nss_mig_getpwnam_r(const char *name,
290298
if (keep_trying && name_len == get_jupytersidmount_length()) {
291299
WRITELOGMESSAGE(LOG_DEBUG, "Checking for jupytersidmount: %s\n", name);
292300
memset(pathbuf, 0, PATH_BUF_LEN);
293-
if (PATH_BUF_LEN ==
301+
if (PATH_BUF_LEN <=
294302
snprintf(pathbuf, PATH_BUF_LEN, "%s/%s",
295303
get_jupytersidmount_home(), name)) {
296304
WRITELOGMESSAGE(LOG_WARNING,

mig/src/libpam-mig/libpam_mig.c

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,14 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags,
526526

527527
/* Basic validation of username before use anywhere in paths or python */
528528

529+
/* IMPORTANT: when constructing strings from unknown input we must use
530+
* snprintf with the actual buffer size as max size parameter and check
531+
* that the returned value was smaller than that size. Any bigger return
532+
* value means that the buffer was written but the input truncated when
533+
* it attempted to write the returned number of bytes.
534+
* https://pubs.opengroup.org/onlinepubs/9799919799/functions/snprintf.html
535+
*/
536+
529537
/* Since we rely on mapping the username to a path on disk,
530538
double check that the name does not contain path traversal attempts
531539
after basic input validation for only safe characters. */
@@ -539,7 +547,7 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags,
539547
} else {
540548
valid_username = true;
541549
/* pUsername is validated enough to be safely used in python calls */
542-
if (USERNAME_MAX_LENGTH ==
550+
if (USERNAME_MAX_LENGTH <=
543551
snprintf(safeUsername, USERNAME_MAX_LENGTH, "%s", pUsername)) {
544552
WRITELOGMESSAGE(LOG_WARNING,
545553
"Safe username construction failed for: %s\n",
@@ -714,7 +722,7 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags,
714722
WRITELOGMESSAGE(LOG_DEBUG, "Checking for sharelink: %s\n", pUsername);
715723
if (strlen(pUsername) == get_sharelink_length()) {
716724
char share_path[MAX_PATH_LENGTH];
717-
if (MAX_PATH_LENGTH ==
725+
if (MAX_PATH_LENGTH <=
718726
snprintf(share_path, MAX_PATH_LENGTH, "%s/%s/%s",
719727
get_sharelink_home(), SHARELINK_SUBDIR, pUsername)) {
720728
WRITELOGMESSAGE(LOG_WARNING,
@@ -780,7 +788,7 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags,
780788
WRITELOGMESSAGE(LOG_DEBUG, "Checking for jobsidmount: %s\n", pUsername);
781789
if (strlen(pUsername) == get_jobsidmount_length()) {
782790
char share_path[MAX_PATH_LENGTH];
783-
if (MAX_PATH_LENGTH ==
791+
if (MAX_PATH_LENGTH <=
784792
snprintf(share_path, MAX_PATH_LENGTH, "%s/%s",
785793
get_jobsidmount_home(), pUsername)) {
786794
WRITELOGMESSAGE(LOG_WARNING,
@@ -860,7 +868,7 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags,
860868
WRITELOGMESSAGE(LOG_DEBUG, "Checking for jupytersidmount: %s\n", pUsername);
861869
if (strlen(pUsername) == get_jupytersidmount_length()) {
862870
char share_path[MAX_PATH_LENGTH];
863-
if (MAX_PATH_LENGTH ==
871+
if (MAX_PATH_LENGTH <=
864872
snprintf(share_path, MAX_PATH_LENGTH, "%s/%s",
865873
get_jupytersidmount_home(), pUsername)) {
866874
WRITELOGMESSAGE(LOG_WARNING,
@@ -952,7 +960,7 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags,
952960
}
953961

954962
char auth_filename[MAX_PATH_LENGTH];
955-
if (MAX_PATH_LENGTH ==
963+
if (MAX_PATH_LENGTH <=
956964
snprintf(auth_filename, MAX_PATH_LENGTH, "%s/.%s/%s", pw->pw_dir,
957965
get_service_dir(pService), PASSWORD_FILENAME)) {
958966
WRITELOGMESSAGE(LOG_WARNING,

0 commit comments

Comments
 (0)