diff --git a/drop_privs.c b/drop_privs.c new file mode 100644 index 0000000..b5a2d69 --- /dev/null +++ b/drop_privs.c @@ -0,0 +1,104 @@ +/* Written by Ricky Zhou + * Copyright (c) 2011 Ricky Zhou + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include "util.h" + +static uid_t saved_euid; +static gid_t saved_egid; + +static gid_t *saved_groups; +static int saved_groups_length; + +int drop_privileges(struct passwd *pw) { + saved_euid = geteuid(); + saved_egid = getegid(); + + saved_groups_length = getgroups(0, NULL); + if (saved_groups_length < 0) { + D (("getgroups: %s", strerror(errno))); + return -1; + } + + if (saved_groups_length > 0) { + saved_groups = malloc(saved_groups_length * sizeof(gid_t)); + if (saved_groups == NULL) { + D (("malloc: %s", strerror(errno))); + return -1; + } + + if (getgroups(saved_groups_length, saved_groups) < 0) { + D (("getgroups: %s", strerror(errno))); + return -1; + } + } + + if (initgroups(pw->pw_name, pw->pw_gid) < 0) { + D (("initgroups: %s", strerror(errno))); + return -1; + } + + if (setegid(pw->pw_gid) < 0) { + D (("setegid: %s", strerror(errno))); + return -1; + } + + if (seteuid(pw->pw_uid) < 0) { + D (("seteuid: %s", strerror(errno))); + return -1; + } + + return 0; +} + +int restore_privileges(void) { + if (seteuid(saved_euid) < 0) { + D (("seteuid: %s", strerror(errno))); + return -1; + } + + if (setegid(saved_egid) < 0) { + D (("setegid: %s", strerror(errno))); + return -1; + } + + if (setgroups(saved_groups_length, saved_groups) < 0) { + D (("setgroups: %s", strerror(errno))); + return -1; + } + + free(saved_groups); + + return 0; +} diff --git a/drop_privs.h b/drop_privs.h new file mode 100644 index 0000000..be79d61 --- /dev/null +++ b/drop_privs.h @@ -0,0 +1,9 @@ +#ifndef __PAM_YUBICO_DROP_PRIVS_H_INCLUDED__ +#define __PAM_YUBICO_DROP_PRIVS_H_INCLUDED__ + +#include + +int drop_privileges(struct passwd *); +int restore_privileges(void); + +#endif diff --git a/pam_yubico.c b/pam_yubico.c index b090d3e..ed7c224 100644 --- a/pam_yubico.c +++ b/pam_yubico.c @@ -34,7 +34,14 @@ #include #include +#include +#include +#include +#include +#include + #include "util.h" +#include "drop_privs.h" /* Libtool defines PIC for shared objects */ #ifndef PIC @@ -125,14 +132,34 @@ check_user_token (struct cfg *cfg, char buf[1024]; char *s_user, *s_token; int retval = 0; + int fd; + struct stat st; FILE *opwfile; - opwfile = fopen (authfile, "r"); - if (opwfile == NULL) - { + fd = open(authfile, O_RDONLY, 0); + if (fd < 0) { DBG (("Cannot open file: %s", authfile)); return retval; - } + } + + if (fstat(fd, &st) < 0) { + DBG (("Cannot stat file: %s", authfile)); + close(fd); + return retval; + } + + if (!S_ISREG(st.st_mode)) { + DBG (("%s is not a regular file", authfile)); + close(fd); + return retval; + } + + opwfile = fdopen(fd, "r"); + if (opwfile == NULL) { + DBG (("fdopen: %s", strerror(errno))); + close(fd); + return retval; + } while (fgets (buf, 1024, opwfile)) { @@ -173,6 +200,18 @@ authorize_user_token (struct cfg *cfg, const char *otp_id) { int retval; + struct passwd *p; + + p = getpwnam (username); + if (p == NULL) { + DBG (("getpwnam: %s", strerror(errno))); + return 0; + } + + if (drop_privileges(p) < 0) { + D (("could not drop privileges")); + return 0; + } if (cfg->auth_file) { @@ -196,6 +235,12 @@ authorize_user_token (struct cfg *cfg, free (userfile); } + if (restore_privileges() < 0) + { + DBG (("could not restore privileges")); + return 0; + } + return retval; } @@ -356,6 +401,7 @@ authorize_user_token_ldap (struct cfg *cfg, return retval; } +#if HAVE_LIBYKPERS_1 static int display_error(pam_handle_t *pamh, char *message) { struct pam_conv *conv; @@ -383,6 +429,7 @@ display_error(pam_handle_t *pamh, char *message) { D(("conv returned: '%s'", resp->resp)); return retval; } +#endif #if HAVE_LIBYKPERS_1 static int @@ -402,6 +449,8 @@ do_challenge_response(pam_handle_t *pamh, struct cfg *cfg, const char *username) int len; char *errstr = NULL; + struct passwd *p; + ret = PAM_AUTH_ERR; flags |= YK_FLAG_MAYBLOCK; @@ -423,7 +472,19 @@ do_challenge_response(pam_handle_t *pamh, struct cfg *cfg, const char *username) DBG(("Loading challenge from file %s", userfile)); - /* XXX should drop root privileges before opening file in user's home directory */ + p = getpwnam (username); + if (p == NULL) { + DBG (("getpwnam: %s", strerror(errno))); + goto out; + } + + /* Drop privileges before opening user file. */ + if (drop_privileges(p) < 0) { + D (("could not drop privileges")); + goto out; + } + + /* XXX may want to check that userfile is a regular file. */ f = fopen(userfile, "r"); if (! load_chalresp_state(f, &state)) @@ -434,6 +495,11 @@ do_challenge_response(pam_handle_t *pamh, struct cfg *cfg, const char *username) goto out; } + if (restore_privileges() < 0) { + DBG (("could not restore privileges")); + goto out; + } + if (! challenge_response(yk, state.slot, state.challenge, state.challenge_len, true, flags, false, buf, sizeof(buf), &response_len)) {