Stefan Krah
2006-Sep-18 11:14 UTC
BSD Auth: set child environment variables requested by login script [PATCH]
Hello, in the BSD Authentication system the login script can request environment variables to be set/unset. The call to auth_close() in auth-passwd.c does change the current environment, but those changes are lost for the child environment. It would be really useful to add some kind of mechanism to get those changes into the child environment. I've added two possible solutions. Both solutions only deal with setenv requests. Please tell me what you think and if there's a chance of adding the feature. Stefan Krah ###################################### Easy way to prepare for testing: ###################################### Index: libexec/login_passwd/login.c ==================================================================RCS file: /cvs/src/libexec/login_passwd/login.c,v retrieving revision 1.8 diff -u -r1.8 login.c --- libexec/login_passwd/login.c 14 Apr 2005 18:33:42 -0000 1.8 +++ libexec/login_passwd/login.c 18 Sep 2006 10:32:00 -0000 @@ -107,6 +107,9 @@ exit(1); } + fprintf(back, BI_SETENV " X_BSD_AUTH_SOME_RESOURCE %d\n", 1024); + fprintf(back, BI_SETENV " TESTVAR %s\n", "bar"); + /* * Read password, either as from the terminal or if the * response mode is active from the caller program. ###################################### Solution 1: ###################################### This is a minimal fix that just whitelists variables starting with X_BSD_AUTH: Index: usr.bin/ssh/auth-bsdauth.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/auth-bsdauth.c,v retrieving revision 1.10 diff -u -r1.10 auth-bsdauth.c --- usr.bin/ssh/auth-bsdauth.c 3 Aug 2006 03:34:41 -0000 1.10 +++ usr.bin/ssh/auth-bsdauth.c 18 Sep 2006 09:32:20 -0000 @@ -24,6 +24,7 @@ */ #include <sys/types.h> +#include <string.h> #ifdef BSD_AUTH #include "xmalloc.h" @@ -32,10 +33,36 @@ #include "auth.h" #include "log.h" #include "buffer.h" +#include "channels.h" +#include "session.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" + +/* + * Set child environment variables starting with "X_BSD_AUTH". + * After the call to auth_close(), these variables are in the + * current environment if the login script has requested them. + */ +void +bsdauth_child_set_env(char ***envp, u_int *envsizep) +{ + extern char **environ; + char name[8*1024]; /* MAXSPOOLSIZE in auth_session_t */ + char *value; + u_int i, namelen; + + for (i = 0; environ[i] != NULL; i++) { + namelen = strcspn(environ[i], "="); + if (namelen + 1 > sizeof(name)) + continue; + snprintf(name, namelen + 1, "%s", environ[i]); + value = environ[i] + namelen + 1; + if (strncmp(name, "X_BSD_AUTH", 10) == 0) + child_set_env(envp, envsizep, name, value); + } +} static void * bsdauth_init_ctx(Authctxt *authctxt) Index: usr.bin/ssh/auth.h ==================================================================RCS file: /cvs/src/usr.bin/ssh/auth.h,v retrieving revision 1.58 diff -u -r1.58 auth.h --- usr.bin/ssh/auth.h 18 Aug 2006 09:15:20 -0000 1.58 +++ usr.bin/ssh/auth.h 18 Sep 2006 09:32:23 -0000 @@ -123,6 +123,10 @@ void krb5_cleanup_proc(Authctxt *authctxt); #endif /* KRB5 */ +#ifdef BSD_AUTH +void bsdauth_child_set_env(char ***envp, u_int *envsizep); +#endif + void do_authentication(Authctxt *); void do_authentication2(Authctxt *); Index: usr.bin/ssh/session.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/session.c,v retrieving revision 1.219 diff -u -r1.219 session.c --- usr.bin/ssh/session.c 29 Aug 2006 10:40:19 -0000 1.219 +++ usr.bin/ssh/session.c 18 Sep 2006 09:32:57 -0000 @@ -844,6 +844,9 @@ child_set_env(&env, &envsize, "KRB5CCNAME", s->authctxt->krb5_ticket_file); #endif +#ifdef BSD_AUTH + bsdauth_child_set_env(&env, &envsize); +#endif if (auth_sock_name != NULL) child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, auth_sock_name); ###################################### Solution 2: ###################################### This one saves the current environment and lets auth_close() do the changes on an empty environment. All setenv requests are honored, unsetenv requests are lost. Index: usr.bin/ssh/auth-bsdauth.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/auth-bsdauth.c,v retrieving revision 1.10 diff -u -r1.10 auth-bsdauth.c --- usr.bin/ssh/auth-bsdauth.c 2006/08/03 03:34:41 1.10 +++ usr.bin/ssh/auth-bsdauth.c 2006/09/18 09:35:52 @@ -24,6 +24,7 @@ */ #include <sys/types.h> +#include <string.h> #ifdef BSD_AUTH #include "xmalloc.h" @@ -32,10 +33,96 @@ #include "auth.h" #include "log.h" #include "buffer.h" +#include "channels.h" +#include "session.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" + +/* copy the current environment. */ +static char ** +bsdauth_env_copy(void) +{ + extern char **environ; + char **copy, *x; + u_int i, len; + + for (i = 0; environ[i] != NULL; i++) + ; + copy = xmalloc((i + 1) * sizeof(char *)); + + for (i = 0; environ[i] != NULL; i++) { + len = strlen(environ[i]); + x = xmalloc(len + 1); + strncpy(x, environ[i], len + 1); + copy[i] = x; + } + copy[i] = NULL; + + return copy; +} + +/* free the copy. */ +void +bsdauth_env_free(Authctxt *authctxt, char **env) +{ + u_int i; + + if (env != NULL && authctxt->auth_env_mod != NULL) { + for (i = 0; env[i] != NULL; i++) + xfree(env[i]); + xfree(env); + } +} + +/* + * Wrapper around auth_close(): auth_close() changes the current environment + * as requested by the login script. To catch the setenv requests, we save + * the current environment and let auth_close() do the changes on an empty + * environment. unsetenv requests are lost. + */ +int +auth_close_do_env(Authctxt *authctxt, auth_session_t *as) +{ + extern char **environ; + char **env_orig; + int ret; + + env_orig = bsdauth_env_copy(); + environ[0] = NULL; + + ret = auth_close(as); + + /* environ now contains all setenv changes done by auth_close(). */ + authctxt->auth_env_mod = environ; + environ = env_orig; + + return ret; +} + +/* modify the child environment according to login script requests. */ +void +bsdauth_child_mod_env(Authctxt *authctxt, char ***envp, u_int *envsizep) +{ + char **env_mod; + char name[8*1024]; /* MAXSPOOLSIZE in auth_session_t */ + char *value; + u_int i, namelen; + + env_mod = authctxt->auth_env_mod; + + if (env_mod != NULL) { + for (i = 0; env_mod[i] != NULL; i++) { + namelen = strcspn(env_mod[i], "="); + if (namelen + 1 > sizeof(name)) + continue; + snprintf(name, namelen + 1, "%s", env_mod[i]); + value = env_mod[i] + namelen + 1; + child_set_env(envp, envsizep, name, value); + } + } +} static void * bsdauth_init_ctx(Authctxt *authctxt) Index: usr.bin/ssh/auth-passwd.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/auth-passwd.c,v retrieving revision 1.40 diff -u -r1.40 auth-passwd.c --- usr.bin/ssh/auth-passwd.c 2006/08/03 03:34:41 1.40 +++ usr.bin/ssh/auth-passwd.c 2006/09/18 09:35:52 @@ -144,7 +144,7 @@ if (as == NULL) return (0); if (auth_getstate(as) & AUTH_PWEXPIRED) { - auth_close(as); + auth_close_do_env(authctxt, as); disable_forwarding(); authctxt->force_pwchange = 1; return (1); @@ -153,7 +153,7 @@ expire_checked = 1; warn_expiry(authctxt, as); } - return (auth_close(as)); + return (auth_close_do_env(authctxt, as)); } } #else Index: usr.bin/ssh/auth.h ==================================================================RCS file: /cvs/src/usr.bin/ssh/auth.h,v retrieving revision 1.58 diff -u -r1.58 auth.h --- usr.bin/ssh/auth.h 2006/08/18 09:15:20 1.58 +++ usr.bin/ssh/auth.h 2006/09/18 09:35:53 @@ -61,6 +61,7 @@ void *kbdintctxt; #ifdef BSD_AUTH auth_session_t *as; + char **auth_env_mod; /* env changes requested by login script */ #endif #ifdef KRB5 krb5_context krb5_ctx; @@ -122,6 +123,12 @@ int auth_krb5_password(Authctxt *authctxt, const char *password); void krb5_cleanup_proc(Authctxt *authctxt); #endif /* KRB5 */ + +#ifdef BSD_AUTH +int auth_close_do_env(Authctxt *authctxt, auth_session_t *as); +void bsdauth_env_free(Authctxt *authctxt, char **env); +void bsdauth_child_mod_env(Authctxt *authctxt, char ***envp, u_int *envsizep); +#endif void do_authentication(Authctxt *); void do_authentication2(Authctxt *); Index: usr.bin/ssh/session.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/session.c,v retrieving revision 1.219 diff -u -r1.219 session.c --- usr.bin/ssh/session.c 2006/08/29 10:40:19 1.219 +++ usr.bin/ssh/session.c 2006/09/18 09:35:56 @@ -844,6 +844,9 @@ child_set_env(&env, &envsize, "KRB5CCNAME", s->authctxt->krb5_ticket_file); #endif +#ifdef BSD_AUTH + bsdauth_child_mod_env(s->authctxt, &env, &envsize); +#endif if (auth_sock_name != NULL) child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, auth_sock_name); @@ -1128,6 +1131,10 @@ * Must take new environment into use so that .ssh/rc, * /etc/ssh/sshrc and xauth are run in the proper environment. */ +#ifdef BSD_AUTH + /* environ points to xmalloc'd memory. */ + bsdauth_env_free(s->authctxt, environ); +#endif environ = env; #ifdef KRB5
Stefan Krah
2006-Sep-18 11:14 UTC
BSD Auth: set child environment variables requested by login script [PATCH]
Hello, in the BSD Authentication system the login script can request environment variables to be set/unset. The call to auth_close() in auth-passwd.c does change the current environment, but those changes are lost for the child environment. It would be really useful to add some kind of mechanism to get those changes into the child environment. I've added two possible solutions. Both solutions only deal with setenv requests. Please tell me what you think and if there's a chance of adding the feature. Stefan Krah ###################################### Easy way to prepare for testing: ###################################### Index: libexec/login_passwd/login.c ==================================================================RCS file: /cvs/src/libexec/login_passwd/login.c,v retrieving revision 1.8 diff -u -r1.8 login.c --- libexec/login_passwd/login.c 14 Apr 2005 18:33:42 -0000 1.8 +++ libexec/login_passwd/login.c 18 Sep 2006 10:32:00 -0000 @@ -107,6 +107,9 @@ exit(1); } + fprintf(back, BI_SETENV " X_BSD_AUTH_SOME_RESOURCE %d\n", 1024); + fprintf(back, BI_SETENV " TESTVAR %s\n", "bar"); + /* * Read password, either as from the terminal or if the * response mode is active from the caller program. ###################################### Solution 1: ###################################### This is a minimal fix that just whitelists variables starting with X_BSD_AUTH: Index: usr.bin/ssh/auth-bsdauth.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/auth-bsdauth.c,v retrieving revision 1.10 diff -u -r1.10 auth-bsdauth.c --- usr.bin/ssh/auth-bsdauth.c 3 Aug 2006 03:34:41 -0000 1.10 +++ usr.bin/ssh/auth-bsdauth.c 18 Sep 2006 09:32:20 -0000 @@ -24,6 +24,7 @@ */ #include <sys/types.h> +#include <string.h> #ifdef BSD_AUTH #include "xmalloc.h" @@ -32,10 +33,36 @@ #include "auth.h" #include "log.h" #include "buffer.h" +#include "channels.h" +#include "session.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" + +/* + * Set child environment variables starting with "X_BSD_AUTH". + * After the call to auth_close(), these variables are in the + * current environment if the login script has requested them. + */ +void +bsdauth_child_set_env(char ***envp, u_int *envsizep) +{ + extern char **environ; + char name[8*1024]; /* MAXSPOOLSIZE in auth_session_t */ + char *value; + u_int i, namelen; + + for (i = 0; environ[i] != NULL; i++) { + namelen = strcspn(environ[i], "="); + if (namelen + 1 > sizeof(name)) + continue; + snprintf(name, namelen + 1, "%s", environ[i]); + value = environ[i] + namelen + 1; + if (strncmp(name, "X_BSD_AUTH", 10) == 0) + child_set_env(envp, envsizep, name, value); + } +} static void * bsdauth_init_ctx(Authctxt *authctxt) Index: usr.bin/ssh/auth.h ==================================================================RCS file: /cvs/src/usr.bin/ssh/auth.h,v retrieving revision 1.58 diff -u -r1.58 auth.h --- usr.bin/ssh/auth.h 18 Aug 2006 09:15:20 -0000 1.58 +++ usr.bin/ssh/auth.h 18 Sep 2006 09:32:23 -0000 @@ -123,6 +123,10 @@ void krb5_cleanup_proc(Authctxt *authctxt); #endif /* KRB5 */ +#ifdef BSD_AUTH +void bsdauth_child_set_env(char ***envp, u_int *envsizep); +#endif + void do_authentication(Authctxt *); void do_authentication2(Authctxt *); Index: usr.bin/ssh/session.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/session.c,v retrieving revision 1.219 diff -u -r1.219 session.c --- usr.bin/ssh/session.c 29 Aug 2006 10:40:19 -0000 1.219 +++ usr.bin/ssh/session.c 18 Sep 2006 09:32:57 -0000 @@ -844,6 +844,9 @@ child_set_env(&env, &envsize, "KRB5CCNAME", s->authctxt->krb5_ticket_file); #endif +#ifdef BSD_AUTH + bsdauth_child_set_env(&env, &envsize); +#endif if (auth_sock_name != NULL) child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, auth_sock_name); ###################################### Solution 2: ###################################### This one saves the current environment and lets auth_close() do the changes on an empty environment. All setenv requests are honored, unsetenv requests are lost. Index: usr.bin/ssh/auth-bsdauth.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/auth-bsdauth.c,v retrieving revision 1.10 diff -u -r1.10 auth-bsdauth.c --- usr.bin/ssh/auth-bsdauth.c 2006/08/03 03:34:41 1.10 +++ usr.bin/ssh/auth-bsdauth.c 2006/09/18 09:35:52 @@ -24,6 +24,7 @@ */ #include <sys/types.h> +#include <string.h> #ifdef BSD_AUTH #include "xmalloc.h" @@ -32,10 +33,96 @@ #include "auth.h" #include "log.h" #include "buffer.h" +#include "channels.h" +#include "session.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" + +/* copy the current environment. */ +static char ** +bsdauth_env_copy(void) +{ + extern char **environ; + char **copy, *x; + u_int i, len; + + for (i = 0; environ[i] != NULL; i++) + ; + copy = xmalloc((i + 1) * sizeof(char *)); + + for (i = 0; environ[i] != NULL; i++) { + len = strlen(environ[i]); + x = xmalloc(len + 1); + strncpy(x, environ[i], len + 1); + copy[i] = x; + } + copy[i] = NULL; + + return copy; +} + +/* free the copy. */ +void +bsdauth_env_free(Authctxt *authctxt, char **env) +{ + u_int i; + + if (env != NULL && authctxt->auth_env_mod != NULL) { + for (i = 0; env[i] != NULL; i++) + xfree(env[i]); + xfree(env); + } +} + +/* + * Wrapper around auth_close(): auth_close() changes the current environment + * as requested by the login script. To catch the setenv requests, we save + * the current environment and let auth_close() do the changes on an empty + * environment. unsetenv requests are lost. + */ +int +auth_close_do_env(Authctxt *authctxt, auth_session_t *as) +{ + extern char **environ; + char **env_orig; + int ret; + + env_orig = bsdauth_env_copy(); + environ[0] = NULL; + + ret = auth_close(as); + + /* environ now contains all setenv changes done by auth_close(). */ + authctxt->auth_env_mod = environ; + environ = env_orig; + + return ret; +} + +/* modify the child environment according to login script requests. */ +void +bsdauth_child_mod_env(Authctxt *authctxt, char ***envp, u_int *envsizep) +{ + char **env_mod; + char name[8*1024]; /* MAXSPOOLSIZE in auth_session_t */ + char *value; + u_int i, namelen; + + env_mod = authctxt->auth_env_mod; + + if (env_mod != NULL) { + for (i = 0; env_mod[i] != NULL; i++) { + namelen = strcspn(env_mod[i], "="); + if (namelen + 1 > sizeof(name)) + continue; + snprintf(name, namelen + 1, "%s", env_mod[i]); + value = env_mod[i] + namelen + 1; + child_set_env(envp, envsizep, name, value); + } + } +} static void * bsdauth_init_ctx(Authctxt *authctxt) Index: usr.bin/ssh/auth-passwd.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/auth-passwd.c,v retrieving revision 1.40 diff -u -r1.40 auth-passwd.c --- usr.bin/ssh/auth-passwd.c 2006/08/03 03:34:41 1.40 +++ usr.bin/ssh/auth-passwd.c 2006/09/18 09:35:52 @@ -144,7 +144,7 @@ if (as == NULL) return (0); if (auth_getstate(as) & AUTH_PWEXPIRED) { - auth_close(as); + auth_close_do_env(authctxt, as); disable_forwarding(); authctxt->force_pwchange = 1; return (1); @@ -153,7 +153,7 @@ expire_checked = 1; warn_expiry(authctxt, as); } - return (auth_close(as)); + return (auth_close_do_env(authctxt, as)); } } #else Index: usr.bin/ssh/auth.h ==================================================================RCS file: /cvs/src/usr.bin/ssh/auth.h,v retrieving revision 1.58 diff -u -r1.58 auth.h --- usr.bin/ssh/auth.h 2006/08/18 09:15:20 1.58 +++ usr.bin/ssh/auth.h 2006/09/18 09:35:53 @@ -61,6 +61,7 @@ void *kbdintctxt; #ifdef BSD_AUTH auth_session_t *as; + char **auth_env_mod; /* env changes requested by login script */ #endif #ifdef KRB5 krb5_context krb5_ctx; @@ -122,6 +123,12 @@ int auth_krb5_password(Authctxt *authctxt, const char *password); void krb5_cleanup_proc(Authctxt *authctxt); #endif /* KRB5 */ + +#ifdef BSD_AUTH +int auth_close_do_env(Authctxt *authctxt, auth_session_t *as); +void bsdauth_env_free(Authctxt *authctxt, char **env); +void bsdauth_child_mod_env(Authctxt *authctxt, char ***envp, u_int *envsizep); +#endif void do_authentication(Authctxt *); void do_authentication2(Authctxt *); Index: usr.bin/ssh/session.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/session.c,v retrieving revision 1.219 diff -u -r1.219 session.c --- usr.bin/ssh/session.c 2006/08/29 10:40:19 1.219 +++ usr.bin/ssh/session.c 2006/09/18 09:35:56 @@ -844,6 +844,9 @@ child_set_env(&env, &envsize, "KRB5CCNAME", s->authctxt->krb5_ticket_file); #endif +#ifdef BSD_AUTH + bsdauth_child_mod_env(s->authctxt, &env, &envsize); +#endif if (auth_sock_name != NULL) child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, auth_sock_name); @@ -1128,6 +1131,10 @@ * Must take new environment into use so that .ssh/rc, * /etc/ssh/sshrc and xauth are run in the proper environment. */ +#ifdef BSD_AUTH + /* environ points to xmalloc'd memory. */ + bsdauth_env_free(s->authctxt, environ); +#endif environ = env; #ifdef KRB5 _______________________________________________ openssh-unix-dev mailing list openssh-unix-dev at mindrot.org http://lists.mindrot.org/mailman/listinfo/openssh-unix-dev