From: thib Date: Mon, 9 Feb 2009 00:33:39 +0000 (+0000) Subject: Implemented fcrondyn client authentication through Linux getsockopt(SO_PEERCRED). X-Git-Tag: ver3_1_0~47 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7695f470e62e855c341af56eb56bbb27e756ee1e;p=fcron Implemented fcrondyn client authentication through Linux getsockopt(SO_PEERCRED). Updated copyright years to include 2009 --- diff --git a/.gitignore b/.gitignore index 6930b2e..f8bad6f 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,4 @@ doc/en/txt/ doc/fr/HTML/ doc/fr/man/ doc/fr/txt/ - +configure diff --git a/config.h.in b/config.h.in index 370d8d4..cb5c726 100644 --- a/config.h.in +++ b/config.h.in @@ -29,6 +29,7 @@ /* *********************************************************** */ +/* ****************************************************************** */ /* ****************************************************************** */ /* beginning of configurable stuff ********************************** */ @@ -133,8 +134,10 @@ /* end of configurable stuff **************************************** */ /* ****************************************************************** */ +/* ****************************************************************** */ +/* ****************************************************************** */ /* ****************************************************************** */ /* *** options which are set by configure script ******************** */ @@ -148,9 +151,14 @@ #undef CFLAGS +/* ****************************************************************** */ /* *** paths *** */ + #undef PROC +/* ****************************************************************** */ +/* *** Compilation options *** */ + /* 1 if we want to compile and install fcrondyn */ #undef FCRONDYN #undef NOLOADAVG @@ -174,6 +182,9 @@ /* special user for the system fcrontab */ #undef SYSFCRONTAB +/* ****************************************************************** */ +/* *** Types *** */ + /* Define to empty if the keyword does not work. */ #undef const @@ -208,6 +219,9 @@ /* Define on System V Release 4. */ #undef SVR4 +/* ****************************************************************** */ +/* *** Functions *** */ + /* Define if you have the crypt function. */ #undef HAVE_CRYPT @@ -289,6 +303,9 @@ /* Define if you have the wait3 system call. */ #undef HAVE_WAIT3 +/* ****************************************************************** */ +/* *** Headers *** */ + /* Define if you have the ANSI C header files. */ #undef STDC_HEADERS @@ -298,6 +315,9 @@ /* Define if your declares struct tm. */ #undef TM_IN_SYS_TIME +/* Define if you have the header file. */ +#undef HAVE_CRED_H + /* Define if you have the header file. */ #undef HAVE_CRYPT_H @@ -346,6 +366,9 @@ /* Define if you have the header file. */ #undef HAVE_STRINGS_H +/* Define if you have the header file. */ +#undef HAVE_SYS_CRED_H + /* Define if you have the header file. */ #undef HAVE_SYS_DIR_H @@ -373,6 +396,9 @@ /* Define if you have the header file. */ #undef HAVE_SYS_TYPES_H +/* Define if you have the header file. */ +#undef HAVE_SYS_UCRED_H + /* Define if you have the header file. */ #undef HAVE_SYS_UN_H @@ -385,6 +411,9 @@ /* Define if you have the header file. */ #undef HAVE_TERMIOS_H +/* Define if you have the header file. */ +#undef HAVE_UCRED_H + /* Define if you have the header file. */ #undef HAVE_UNISTD_H @@ -410,6 +439,9 @@ #define O_SYNC O_FSYNC #endif +/* ****************************************************************** */ +/* *** Size of *** */ + /* These SIZEOF_* constants are defined by the AC_CHECK_SIZEOF macro */ #undef SIZEOF_TIME_T #undef SIZEOF_PID_T diff --git a/configure b/configure index e874790..9477261 100755 --- a/configure +++ b/configure @@ -1761,7 +1761,7 @@ _ACEOF VERSION="$vers" -copyright_quoted="\"2000-2008\"" +copyright_quoted="\"2000-2009\"" cat >>confdefs.h <<_ACEOF #define COPYRIGHT_QUOTED $copyright_quoted _ACEOF @@ -14296,6 +14296,245 @@ _ACEOF fi + + + +for ac_header in cred.h sys/cred.h ucred.h sys/ucred.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +else + # Is the header compilable? +{ echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6; } + +# Is the header present? +{ echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + + ;; +esac +{ echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_func in getpeerucred +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval echo '${'$as_ac_var'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + + fcron_enable_checks=yes # Check whether --enable-checks was given. if test "${enable_checks+set}" = set; then diff --git a/configure.in b/configure.in index 0a8b973..0118162 100644 --- a/configure.in +++ b/configure.in @@ -16,7 +16,7 @@ AC_DEFINE_UNQUOTED(VERSION_QUOTED, $vers_quoted) VERSION="$vers" AC_SUBST(VERSION) -copyright_quoted="\"2000-2008\"" +copyright_quoted="\"2000-2009\"" AC_DEFINE_UNQUOTED(COPYRIGHT_QUOTED, $copyright_quoted) @@ -137,6 +137,7 @@ if test "$crypt" -eq "1"; then fi +dnl --- sockets dnl Check for post-Reno style struct sockaddr AC_CACHE_CHECK([for sa_len], ac_cv_sa_len, @@ -148,6 +149,10 @@ if test $ac_cv_sa_len = yes; then AC_DEFINE(HAVE_SA_LEN) fi +dnl --- Socket authentication +AC_CHECK_HEADERS(cred.h sys/cred.h ucred.h sys/ucred.h) +AC_CHECK_FUNCS(getpeerucred) + dnl --------------------------------------------------------------------- dnl Check for fcron more specific stuffs (paths, progs, ...) dnl --------------------------------------------------------------------- diff --git a/doc/en/changes.sgml b/doc/en/changes.sgml index e69bd6b..f1aa073 100644 --- a/doc/en/changes.sgml +++ b/doc/en/changes.sgml @@ -13,6 +13,16 @@ A copy of the license is included in gfdl.sgml. Changes + + From version 3.0.5 to 3.1.0 + + Pass fcrondyn client credentials through the socket when possible so as the user doesn't need to type his password when using fcrondyn. + + + Code clean-up: Implemented generic unordred list for lavgq / exeq. + + + From version 3.0.4 to 3.0.5 @@ -1151,4 +1161,4 @@ mode: sgml sgml-parent-document:("fcron-doc.sgml" "book" "chapter" "sect1" "") End: --> - \ No newline at end of file + diff --git a/fcrondyn.c b/fcrondyn.c index 8dd211e..43c42bd 100644 --- a/fcrondyn.c +++ b/fcrondyn.c @@ -50,7 +50,7 @@ int interactive_mode(int fd); int talk_fcron(char *cmd_str, int fd); int parse_cmd(char *cmd_str, long int **cmd, int *cmd_len); int connect_fcron(void); -int authenticate_user(int fd); +int authenticate_user_password(int fd); /* command line options */ #ifdef DEBUG @@ -359,7 +359,7 @@ parse_cmd(char *cmd_str, long int **cmd, int *cmd_len) } int -authenticate_user(int fd) +authenticate_user_password(int fd) /* authenticate user */ { char *password = NULL; @@ -426,12 +426,15 @@ connect_fcron(void) if ( connect(fd, (struct sockaddr *) &addr, sun_len) < 0 ) die_e("Cannot connect() to fcron (check if fcron is running)"); - if ( authenticate_user(fd) == ERR ) { - fprintf(stderr, "Invalid password or too many authentication failures" +/* Nothing to do on the client side if we use SO_PASSCRED */ +#ifndef SO_PASSCRED + if ( authenticate_user_password(fd) == ERR ) { + fprintf(stderr, "Invalid password or too many authentication failures" " (try to connect later).\n(In the later case, fcron rejects all" " new authentication during %d secs)\n", AUTH_WAIT); die("Unable to authenticate user"); } +#endif /* SO_PASSCRED */ return fd; diff --git a/global.h b/global.h index 14fcdfd..96f3edc 100644 --- a/global.h +++ b/global.h @@ -112,6 +112,19 @@ #include #endif +#ifdef HAVE_CRED_H +#include +#endif +#ifdef HAVE_UCRED_H +#include +#endif +#ifdef HAVE_SYS_CRED_H +#include +#endif +#ifdef HAVE_SYS_UCRED_H +#include +#endif + #ifdef HAVE_LIBPAM #include "pam.h" #endif diff --git a/socket.c b/socket.c index 4c1e192..bfe973a 100644 --- a/socket.c +++ b/socket.c @@ -34,7 +34,8 @@ void remove_connection(struct fcrondyn_cl **client, struct fcrondyn_cl *prev_client); void exe_cmd(struct fcrondyn_cl *client); -void auth_client(struct fcrondyn_cl *client); +void auth_client_password(struct fcrondyn_cl *client); +void auth_client_so_passcred(struct fcrondyn_cl *client); void cmd_ls(struct fcrondyn_cl *client, long int *cmd, int fd, int is_root); void print_fields(int fd, unsigned char *details); void print_line(int fd, struct cl_t *line, unsigned char *details, pid_t pid, int index, @@ -196,9 +197,52 @@ init_socket(void) } +#ifdef SO_PASSCRED void -auth_client(struct fcrondyn_cl *client) - /* check client identity */ +auth_client_so_passcred(struct fcrondyn_cl *client) + /* check client identity by reading its credentials from the socket + * using getsockopt(SO_PASSCRED). + * Sets client->fcl_user on success, don't do anything on failure */ +{ + const int true = 1; + /* There is no ucred.h (or equivalent) on linux to define struct ucred (!!) + * so we do it here */ +#if ! ( defined(HAVE_CRED_H) && defined(HAVE_UCRED_H) \ + && defined(HAVE_SYS_CRED_H) && defined(HAVE_SYS_UCRED_H) ) + struct ucred { + pid_t pid; + uid_t uid; + gid_t gid; + }; +#endif + struct ucred cred; + socklen_t cred_size = sizeof(cred); + struct passwd *p_entry = NULL; + + setsockopt(client->fcl_sock_fd, SOL_SOCKET, SO_PASSCRED, &true, sizeof(true)); + if ( getsockopt(client->fcl_sock_fd, SOL_SOCKET, SO_PEERCRED, + &cred, &cred_size) != 0) { + error_e("Could not get client credentials using getsockopt(SO_PEERCRED)"); + return; + } + + p_entry = getpwuid(cred.uid); + if ( p_entry == NULL ) { + error_e("Could not find password entry for uid %d", cred.uid); + return; + } + + /* Successfully identified user: */ + client->fcl_user = strdup2(p_entry->pw_name); + + explain("Client's pid=%d, uid=%d, gid=%d username=%s\n", cred.pid, cred.uid, cred.gid, client->fcl_user); + +} +#endif /* SO_PASSCRED */ + +void +auth_client_password(struct fcrondyn_cl *client) + /* check client identity by asking him to input his password */ { char *pass_cry = NULL; char *pass_sys = NULL; @@ -224,7 +268,7 @@ auth_client(struct fcrondyn_cl *client) #endif /* */ - debug("auth_client() : socket : %d", client->fcl_sock_fd); + debug("auth_client_password() : socket : %d", client->fcl_sock_fd); /* */ /* we need to limit auth failures : otherwise fcron may be used to "read" @@ -239,7 +283,7 @@ auth_client(struct fcrondyn_cl *client) return; } - /* password is stored after user name */ + /* the password is stored after the user name */ pass_str = &( (char *)client->fcl_cmd ) [ strlen( (char*)client->fcl_cmd ) + 1 ]; if ( (pass_cry = crypt(pass_str, pass_sys)) == NULL ) { error_e("could not crypt()"); @@ -781,7 +825,8 @@ check_socket(int num) if ( FD_ISSET(listen_fd, &read_set) ) { debug("got new connection ..."); - if ((fd = accept(listen_fd, (struct sockaddr *)&client_addr, &addr_len)) == -1) { + fd = accept(listen_fd, (struct sockaddr *)&client_addr, &addr_len); + if ( fd == -1 ) { error_e("could not accept new connection : isset(listen_fd = %d) = %d", listen_fd, FD_ISSET(listen_fd, &read_set)); } @@ -811,6 +856,10 @@ check_socket(int num) fcrondyn_cl_num += 1; debug("Added connection fd : %d - %d connections", fd, fcrondyn_cl_num); + +#ifdef SO_PASSCRED + auth_client_so_passcred(client); +#endif /* SO_PASSCRED */ } } } @@ -853,7 +902,7 @@ check_socket(int num) client->fcl_cmd = buf_int; if ( client->fcl_user == NULL ) /* not authenticated yet */ - auth_client(client); + auth_client_password(client); else { /* we've just read a command ... */ client->fcl_idle_since = now;