From d4a39b5dd5b9d532f31ebe7e7c1a7bca929ae5d4 Mon Sep 17 00:00:00 2001 From: "T. R. Bernstein" <137705289+trbernstein@users.noreply.github.com> Date: Fri, 17 Oct 2025 23:41:13 +0200 Subject: [PATCH] Use c app to call brew --- modules/04-install-brew.sh | 107 ++++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 32 deletions(-) diff --git a/modules/04-install-brew.sh b/modules/04-install-brew.sh index 85ef05f..887c7a0 100755 --- a/modules/04-install-brew.sh +++ b/modules/04-install-brew.sh @@ -24,17 +24,6 @@ function ensureUserIsInAdminGroup() { dseditgroup -o edit -a "${username}" -t user admin } -function ensureUserCanRunPasswordlessSudo() { - local username=$1 - local sudoersFile="/etc/sudoers.d/no-auth-sudo-for-${username}" - [[ -f ${sudoersFile} ]] && return - cat <<- SUDOERS > "${sudoersFile}" - Defaults:${username} !authenticate - SUDOERS - chown root:wheel "${sudoersFile}" || return 10 - chmod u=rw,g=r,o= "${sudoersFile}" || return 20 -} - function getFirstFreeRoleAccountID() { local minUserID=450 local maxUserID=499 @@ -96,33 +85,88 @@ function getHomebrewRepositoryPath() { function createBrewCallerScript() { ensureLocalBinFolder - local uname_machine=$(/usr/bin/uname -m) local username=${homebrew_username} local homebrewRepositoryPath="$(getHomebrewRepositoryPath)" local brewCallerPath="${homebrewRepositoryPath}/bin/brew-caller" local brewCallerSymlink="/usr/local/bin/brew" - [[ ${uname_machine} == "arm64" ]] && brewCallerPath="${brewCallerSymlink}" [ -f "${brewCallerPath}" ] && rm "${brewCallerPath}" - cat <<- BREWCALLER > ${brewCallerPath} - #!/usr/bin/env zsh - if [ "\$(id -un)" != "${username}" ]; then - echo 'brew will be run as ${username} user.' >&2 - sudo -E -u "${username}" "\$0" "\$@" - exit \$? - fi - export HOMEBREW_CACHE="${homebrew_cache}" - export HOMEBREW_LOGS="${homebrew_log}" - export HOMEBREW_CASK_OPTS="--no-quarantine \${HOMEBREW_CASK_OPTS}" - export HOMEBREW_NO_AUTO_UPDATE=1 - export HOMEBREW_NO_ANALYTICS=1 - export HOMEBREW_NO_ANALYTICS_THIS_RUN=1 - export HOMEBREW_NO_ANALYTICS_MESSAGE_OUTPUT=1 - umask 002 - "${homebrewRepositoryPath}/bin/brew" "\$@" + cat <<- BREWCALLER | clang -x c -O2 -Wall -o "${brewCallerPath}" - + #include + #include + #include + #include + #include + #include + #include + #include + #include + + extern char **environ; + + char *format_env(const char *env_name, const char *format) { + if (!env_name || !format) return NULL; + const char *value = getenv(env_name); + if (!value) return NULL; + int formatted_len = snprintf(NULL, 0, format, value); + if (formatted_len < 0) return NULL; + char *formatted_value = malloc(formatted_len + 1); + if (!formatted_value) return NULL; + snprintf(formatted_value, formatted_len + 1, format, value); + size_t result_len = strlen(env_name) + 1 + formatted_len; + char *result = malloc(result_len + 1); + if (!result) { free(formatted_value); return NULL; } + sprintf(result, "%s=%s", env_name, formatted_value); + free(formatted_value); + return result; + } + + #define HOMEBREW_USER "${username}" + #define HOMEBREW_DIR "${homebrewRepositoryPath}/bin" + #define BREW_PATH HOMEBREW_DIR "/brew" + + int main(int argc, char *argv[]) { + struct passwd *pw; + uid_t target_uid; + gid_t target_gid; + + pw = getpwnam(HOMEBREW_USER); + if (!pw) { fprintf(stderr, "Error: user %s not found\n", HOMEBREW_USER); return 1; } + target_uid = pw->pw_uid; + target_gid = pw->pw_gid; + + char *new_environ[] = { + "PATH=" HOMEBREW_DIR "/usr/bin:/bin", + "HOMEBREW_CACHE=${homebrew_cache}", + "HOMEBREW_LOGS=${homebrew_log}", + format_env("HOME", "%s"), + format_env("HOMEBREW_CASK_OPTS", "--no-quarantine %s"), + "HOMEBREW_NO_AUTO_UPDATE=1", + "HOMEBREW_NO_ANALYTICS=1", + "HOMEBREW_NO_ANALYTICS_THIS_RUN=1", + "HOMEBREW_NO_ANALYTICS_MESSAGE_OUTPUT=1", + NULL + }; + + if (setegid(target_gid) != 0) { fprintf(stderr, "setegid(%d): %s\n", target_gid, strerror(errno)); return 1; } + + if (seteuid(target_uid) != 0) { fprintf(stderr, "seteuid(%d): %s\n", target_uid, strerror(errno)); return 1; } + + char **newargv = malloc((argc + 1) * sizeof(char *)); + if (!newargv) { fprintf(stderr, "malloc failed for new argv\n"); return 1; } + + newargv[0] = (char *)BREW_PATH; + for (int i = 1; i < argc; ++i) newargv[i] = argv[i]; + newargv[argc] = NULL; + umask(0002); + + execve(BREW_PATH, newargv, new_environ); + fprintf(stderr, "execv(%s) failed: %s\n", BREW_PATH, strerror(errno)); + return 1; + } BREWCALLER chown root:admin ${brewCallerPath} - chmod ug+x,o-x ${brewCallerPath} - [[ ${uname_machine} == "arm64" ]] || ln -s "${brewCallerPath}" "${brewCallerSymlink}" + chmod 4550 ${brewCallerPath} + ln -s "${brewCallerPath}" "${brewCallerSymlink}" } function createBrewPeriodicScript() { @@ -214,7 +258,6 @@ function configure_system() { lop -y h1 -- -i 'Install System Homebrew' createHomebrewUserIfNeccessary || return 10 indicateActivity 'Ensure Homebrew user is in admin group' ensureUserIsInAdminGroup ${homebrew_username} || return 11 - indicateActivity 'Ensure Homebrew user can run passwordless sudo' ensureUserCanRunPasswordlessSudo ${homebrew_username} || return 12 ensureHomebrewCacheDirectory || return 13 ensureHomebrewLogDirectory || return 14 indicateActivity 'Install Homebrew core' installHomebrewCore || return 15