Skip to content

pppd: avoid TOCTOU in protected file access#592

Open
LGA1150 wants to merge 1 commit into
ppp-project:masterfrom
LGA1150:fd
Open

pppd: avoid TOCTOU in protected file access#592
LGA1150 wants to merge 1 commit into
ppp-project:masterfrom
LGA1150:fd

Conversation

@LGA1150
Copy link
Copy Markdown
Contributor

@LGA1150 LGA1150 commented May 27, 2026

Note: this is an AI-assisted commit, which I have not tested.

The proper way to fix TOCTOU is to avoid multiple evaluations of a path. Eliminate the check-then-open antipattern when opening files. Instead, open files first and validate the resulting file descriptor, and run scripts through fexecve().

This fixes the TOCTOU window, and avoids overly strict directory ownership checks that could break existing configurations.

Related issue: #591

Copy link
Copy Markdown
Contributor

@jkroonza jkroonza left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using FILE* where not needed. I don't like the approach, especially for the fexec side, will see if I can find cycles on this.

@LGA1150
Copy link
Copy Markdown
Contributor Author

LGA1150 commented May 28, 2026

I've refactored ppp_check_access and check_access. They now check the file descriptor instead of file name.

@LGA1150 LGA1150 force-pushed the fd branch 2 times, most recently from 6a29b48 to faaf527 Compare May 28, 2026 09:55
@jkroonza
Copy link
Copy Markdown
Contributor

@LGA1150 I'm going to unsubscribe myself for the moment, please do @jkroonza me once you believe you're done.

Thank you for moving this in a better direction. I see you're still pushing quite a lot so please @jkroonza when you're looking for specific input or believe you're done. I don't have commit rights but I'll happily help review.

You'll note the original patch introduced a few extra char* variables to hold the absolute filename and to eliminate symlink attacks (which with your patch is no longer needed at all).

In order to simplify things long-term (not for the patch itself), you may want to eliminate those again as well. Eg, filename = path_upapfile. There should no longer be a reason to copy the pointer at all, just use path_upapfile as is, for example.

@LGA1150
Copy link
Copy Markdown
Contributor Author

LGA1150 commented May 29, 2026

@jkroonza The filename local variable was not introduced in said commit, so the simplification is unrelated to this pull request.

@LGA1150 LGA1150 requested a review from jkroonza May 29, 2026 01:30
@LGA1150 LGA1150 force-pushed the fd branch 2 times, most recently from fafcfba to 49a02b2 Compare May 29, 2026 01:41
The proper way to fix TOCTOU is to avoid multiple evaluations of a
path. Eliminate the check-then-open antipattern when opening files.
Instead, open files first and validate the resulting file descriptor,
and run scripts through fexecve().

This fixes the TOCTOU window, and avoids overly strict directory
ownership checks that could break existing configurations.

Signed-off-by: Qingfang Deng <dqfext@gmail.com>
Copy link
Copy Markdown
Contributor

@jkroonza jkroonza left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of my comments are blockers @paulusmack I think this is good but can be improved - I'm happy to perform the cleanups in a follow-up commit which I think would be in order anyway to find and sort out a few cases which really aught to be const char* where char* is currently used.

Comment thread pppd/utils.c

err:
free(rpath);
return 0;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: Since we no longer cleanup anything this goto can go away, but I'm not hard objecting to the idiom used here.

Comment thread pppd/auth.c
static int set_noauth_addr (char **);
static int set_permitted_number (char **);
static void check_access (FILE *, char *);
static void check_access (int, const char *);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this was still in place after previous patch set, but since this is now already checked by ppp_check_access I think this function can go away entirely. I think you modified this now based on my previous review noting that ppp_check_access was then taking a FILE*.

I think in order to not just fix the patch but actually make progress too regarding simplification, kill this function entirely too please unless I'm being stupid and it does actually check something which I'm missing, but as far as I can tell it doesn't actually enforce anything other than warning about the file being group/other writeable - something which is now enforced already by ppp_check_access.

Comment thread pppd/auth.c
*/
if (!ppp_check_access(path_upapfile, &filename, 0, 0))
return UPAP_AUTHNAK;
filename = path_upapfile;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get that this is a straight up revert, so if you don't mind, please fix in this commit - so either you can clean up now or I'll post a follow-up clean up patch where I can find this idiom.

Cases like this is not ideal either so a follow up commit trying to trace and fix all of them is possibly better:

2546 have_eaptls_secret_client()

2550 char *filename;

2567 filename = PPP_PATH_EAPTLSCLIFILE;

Plainly we're assigning const char* there to char* which can lead to hard to troubleshoot issues.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A subsequent clean-up patch could constify all path variables as long as parameters.

Comment thread pppd/auth.c
check_access(f, filename);
int fd = fileno(f);

if (!ppp_check_access(fd, filename, 0)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once check_access() goes away I think fileno() inline here is fine avoiding the extra variable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants