diff options
| -rw-r--r-- | .gitmodules | 3 | ||||
| -rw-r--r-- | README.md | 31 | ||||
| -rw-r--r-- | appinfo/app.php | 30 | ||||
| -rw-r--r-- | appinfo/info.xml | 15 | ||||
| -rw-r--r-- | members_src/Makefile | 42 | ||||
| -rwxr-xr-x | members_src/members | bin | 0 -> 41408 bytes | |||
| -rw-r--r-- | members_src/members.1 | 59 | ||||
| -rw-r--r-- | members_src/members.cc | 510 | ||||
| -rw-r--r-- | members_src/members.o | bin | 0 -> 62536 bytes | |||
| m--------- | pwauth | 0 | ||||
| -rw-r--r-- | user_pam.php | 91 |
11 files changed, 781 insertions, 0 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..7eea220 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "pwauth"] + path = pwauth + url = https://github.com/phokz/pwauth diff --git a/README.md b/README.md new file mode 100644 index 0000000..d9e58c9 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +Based on [User PAM backend 0.1](https://apps.owncloud.com/content/show.php/User+PAM+backend?content=174684). + +Edit `pwauth/pwauth/config.h` and set the web server's user id: + +```c +#define SERVER_UIDS x +``` + +Compile pwauth: `cd pwauth/pwauth && make`. + +Compile members: `cd members_src && make`. + +Copy both binaries to some directory (e.g. `/usr/local/bin`), configure plugin's `appinfo/app.php` + +Set suid-flag to allow the script to read `/etc/passwd`: `chmod u+s pwauth` + +If something does not work, start debugging by adding this line in `user_pam.php`: + +```diff +if (fwrite($handle, "$uid\n$password\n") === false) + return false + +$result = pclose( $handle ); +if (0 === $result) + return $uid; + ++ error_log($result); +return false; +``` + +Error codes are at the bottom of pwauth's `INSTALL`. diff --git a/appinfo/app.php b/appinfo/app.php new file mode 100644 index 0000000..9e783b1 --- /dev/null +++ b/appinfo/app.php @@ -0,0 +1,30 @@ +<?php +/** + * @author CSharplie + * + * @copyright Copyright (c) 2016, CSharplie + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +require_once('apps/user_pam/user_pam.php'); + +define('OC_USER_BACKEND_PAM_PATH', '/etc/nixos/bin/pwauth'); +define('OC_USER_BACKEND_MEMBERS_PATH', '/etc/nixos/bin/members'); +define('OC_USER_BACKEND_PAM_GROUP', 'users'); + +OC_User::useBackend(new \OCA\user_pam\USER_PAM()); + +?> diff --git a/appinfo/info.xml b/appinfo/info.xml new file mode 100644 index 0000000..b4ab7ac --- /dev/null +++ b/appinfo/info.xml @@ -0,0 +1,15 @@ +<?xml version="1.0"?> +<info> + <id>user_pam</id> + <name>Unix PAM backend</name> + <description>Authenticate users by PAM according a unix group - Based on Unix user backend app</description> + <licence>MIT</licence> + <author>CSharplie</author> + <version>0.1</version> + <dependencies> + <owncloud min-version="8.0" max-version="9.0" /> + </dependencies> + <types> + <logging/> + </types> +</info> diff --git a/members_src/Makefile b/members_src/Makefile new file mode 100644 index 0000000..fa194a5 --- /dev/null +++ b/members_src/Makefile @@ -0,0 +1,42 @@ +#this is the -*- makefile -*- for members. +# +# members is the complement of groups: whereas groups shows the groups a +# specified user belongs to, members shows users belonging to a specified +# group. + +# Copyright (c) 1997 by Jim Lynch. +# This software comes with NO WARRANTY WHATSOEVER. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 dated June, 1991, or, at your +# option, any LATER version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# On Debian Linux systems, the complete text of the GNU General +# Public License can be found in `/usr/doc/copyright/GPL' (on some +# installations) or /usr/share/common-licenses/GPL (on newer +# ones). + +CPPFLAGS = -g -Wno-deprecated + +members: members.o + g++ -o members members.o + +main.o: members.cc + +clean: + rm -f members *.o *~ core + +install: members + cp members $(DESTDIR)/usr/bin/members + chmod 755 $(DESTDIR)/usr/bin/members + chown bin.bin $(DESTDIR)/usr/bin/members diff --git a/members_src/members b/members_src/members Binary files differnew file mode 100755 index 0000000..1b5fc5e --- /dev/null +++ b/members_src/members diff --git a/members_src/members.1 b/members_src/members.1 new file mode 100644 index 0000000..b473ffa --- /dev/null +++ b/members_src/members.1 @@ -0,0 +1,59 @@ +.TH MEMBERS 1 +.\" NAME should be all caps, SECTION should be 1-8, maybe w/ subsection +.\" other parms are allowed: see man(7), man(1) +.SH NAME +members \- outputs members of a group +.SH SYNOPSIS +.B members +.I "groupname" +.SH "DESCRIPTION" +This manual page documents briefly the +.BR members +commands. +This manual page was written for the Debian GNU/Linux distribution. +.PP +.B members +is a program that sends a space-separated list of secondary member names +to its standard output. +.SH OPTIONS +The programs follow the usual GNU command line syntax, with long +options starting with two dashes (`-'). +A summary of options is included below. +.\" For a complete description, see the Info files. +.TP +.B \-a, \-\-all +Show all group members on one line. This is the default. +.TP +.B \-p, \-\-primary +Show only primary group members. +.TP +.B \-s, \-\-secondary +Show only secondary group members. +.TP +.B \-t, \-\-two-lines +Send two lines to standard output. First line is primary members, second +line is secondary members. NOTE: This always displays two lines, even if +there are no members at all. +.TP +.B \-h, \-\-help +Show summary of options. +.\" .TP +.\" .B \-v, \-\-version +.\" Show version of program. +.SH DIAGNOSTICS +.PP +Exit status is 0 (i.e. "success") if the group was found, +and 1 (i.e., "failure") if the group was not found. +.PP +Technically, the exit status hinges on the output of +.B getgrnam(3) +as follows: if +.B getgrnam(3) +returns a null pointer, the exit status is 1, and 0 otherwise. +.SH BUGS +I don't know of any! If you find one, please let me know! +.SH "SEE ALSO" +groups(1) +.SH AUTHOR +This manual page was written by Jim Lynch <jim@laney.edu>, +for the Debian GNU/Linux system (but may be used by others). diff --git a/members_src/members.cc b/members_src/members.cc new file mode 100644 index 0000000..a2d0532 --- /dev/null +++ b/members_src/members.cc @@ -0,0 +1,510 @@ +// members is the complement of groups: whereas groups shows the groups a +// specified user belongs to, members shows users belonging to a specified +// group. + +// PRESENTLY WORKING ON FULL OPTION PARSING (search for /**/ for work pt) + +// Copyright (c) 1997 by Jim Lynch. +// This software comes with NO WARRANTY WHATSOEVER. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 dated June, 1991, or, at your +// option, any LATER version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// On Debian Linux systems, the complete text of the GNU General +// Public License can be found in `/usr/doc/copyright/GPL' (on some +// installations) or /usr/share/common-licenses/GPL (on newer +// ones). + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> + +#include <cstdio> +#include <iostream> + + using std::cerr; + using std::ostream; + using std::endl; + using std::cout; + using std::hex; + using std::endl; + +#include <grp.h> +#include <pwd.h> +#include <sys/types.h> +#include <getopt.h> + +#define ALL_OPT 'a' +#define PRIMARY_OPT 'p' +#define SECONDARY_OPT 's' +#define TWO_LINES_OPT 't' +#define HELP_OPT 'h' + +#define END_SHORT_OPT '\0' +#define END_LONG_OPT {(char *) 0, 0, (int *) 0, '\0'} + +const char short_blank = END_SHORT_OPT; +const struct option long_blank = END_LONG_OPT; + +struct option longopts[] = +{ + {"all", no_argument, (int *) 0, ALL_OPT }, + {"primary", no_argument, (int *) 0, PRIMARY_OPT }, + {"secondary", no_argument, (int *) 0, SECONDARY_OPT }, + {"two-lines", no_argument, (int *) 0, TWO_LINES_OPT }, + {"help", no_argument, (int *) 0, HELP_OPT }, + END_LONG_OPT +}; + +char opt_string[] = +{ + ALL_OPT, + PRIMARY_OPT, + SECONDARY_OPT, + TWO_LINES_OPT, + HELP_OPT, + END_SHORT_OPT +}; + +int optionlen(struct option *option_array) +{ + int result = 0; + + while(option_array[result].name != NULL) + result++; + + return result; +} + +void remove_short_option(char option, char *option_array) +{ + int i; + int found_index = -1; /* index of found index !or! -1 if none */ + int size = strlen(option_array); + + for(i = 0; i < size; i++) + { + if(option_array[i] == option) + { + found_index = i; + break; + } + } + + if(found_index != -1) + { + if(found_index != size - 1) /* not last item? */ + { + /* replace found item with last item */ + + option_array[found_index] = option_array[size - 1]; + } + + /* blank out last item */ + option_array[size - 1] = short_blank; + } +} + +void remove_long_option(char option, struct option *option_array) +{ + int i; + int found_index = -1; /* index of found index !or! -1 if none */ + int size = optionlen(option_array); + + for(i = 0; i < size; i++) + { + if(option_array[i].val == option) + { + found_index = i; + break; + } + } + + if(found_index != -1) + { + if(found_index != size - 1) /* not last item? */ + { + /* replace found item with last item */ + + option_array[found_index] = option_array[size - 1]; + } + + /* blank out last item */ + option_array[size - 1] = long_blank; + } +} + +void print_option_array(struct option *option_array) +{ + int i = 0; + + while(option_array[i].val != short_blank) + { + if(option_array[i].flag != NULL) + { + cout + << "{" + << option_array[i].name << ", " + << option_array[i].has_arg << ", " + << (*option_array[i].flag) << ", '" + << ( (char) option_array[i].val ) << "'=0x" + << std::hex << option_array[i].val << "}" << std::dec << endl; + } + else + { + cout + << "{" + << option_array[i].name << ", " + << option_array[i].has_arg << ", NULL, '" + << ( (char) option_array[i].val ) << "'=0x" + << std::hex << option_array[i].val << "}" << std::dec << endl; + } + + i++; + } +} + +void disable_option(char *opt_chars) +{ + while(*opt_chars) + { + remove_short_option(*opt_chars, opt_string); + remove_long_option(*opt_chars, longopts); + + opt_chars++; + } +} + +void usage(ostream &out, char *name) +{ + out << endl << "Usage: " << name; + out << " [ -apst --all --primary --secondary --two-lines ] <group>" << endl; +} + +//receives: +// - C string containing group name +// - pointer to int containing (!assumed initialized!) flag +// indicating whether a member name was printed on this line. +// (if true, at least one member had been printed) +// returns true if group found. + +int display_primaries(const char *groupString, int *printedOne) +{ + int result = 0; + struct group *theGroup; + + theGroup = getgrnam(groupString); + + if(theGroup) + { + struct passwd *thePasswd; + + setpwent(); + + thePasswd = getpwent(); + + if(thePasswd) + { + thePasswd = getpwent(); + + while(thePasswd) + { + if(thePasswd->pw_gid == theGroup->gr_gid) + { + // if there is at least one, print it without any space + // if there is at least one more, print space then it + + if(*printedOne) + cout << " "; + else + *printedOne = 1; + + cout << thePasswd->pw_name; + } + + thePasswd = getpwent(); + } + } + + endpwent(); + + result = 1; + } + + return result; +} + +//receives: +// - C string containing group name +// - pointer to int containing (!assumed initialized!) flag +// indicating whether a member name was printed on this line. +// (if true, at least one member had been printed) +// returns true if group found. + +int display_secondaries(const char *groupString, int *printedOne) +{ + int result = 0; + struct group *theGroup; + + theGroup = getgrnam(groupString); + + if(theGroup) + { + char **member = theGroup->gr_mem; + + result = 1; + + // if there is at least one, print it without any space + // if there is at least one more, print space then it + + while(*member) + { + if(*printedOne) + cout << " "; + else + *printedOne = 1; + + cout << *member++; + } + } + + return result; +} + +int main(int argc, char *argv[]) +{ + int result = 1; // pessimistic since we do something if success + int first_nonopt = 1; /* index of the first non-option in argv */ + int is_bad_opt = 0; /* true if unrecognized option */ + int getopt_result; + int longindex = 0; + + int wants_help = 0; /* boolean from options */ + int wants_all_members = 0; + int wants_primary = 0; + int wants_secondary = 0; + int wants_two_lines = 0; + + getopt_result = + getopt_long + ( + argc, + argv, + opt_string, + longopts, + &longindex + ); + + while(getopt_result != EOF) + { + /* process opts here */ + + switch(getopt_result) + { + case ALL_OPT: + wants_all_members = 1; + + disable_option("apst"); + + break; + + case PRIMARY_OPT: + wants_primary = 1; + + disable_option("apst"); + + break; + + case SECONDARY_OPT: + wants_secondary = 1; + + disable_option("apst"); + + break; + + case TWO_LINES_OPT: + wants_two_lines = 1; + + disable_option("apst"); + + break; + + case HELP_OPT: + wants_help = 1; + + disable_option("apst"); + + break; + + case '?': + default: + is_bad_opt = 1; + break; + } + + if(is_bad_opt) + break; + else + getopt_result = + getopt_long + ( + argc, + argv, + opt_string, + longopts, + &longindex + ); + } + + first_nonopt = optind; + +#ifdef SKIP + // display results of option parsing + cout << "wants_help: " << ( wants_help ? "yes" : "no" ) << endl; + cout << "wants_all_members: " << ( wants_all_members ? "yes" : "no" ) << endl; + cout << "wants_primary: " << ( wants_primary ? "yes" : "no" ) << endl; + cout << "wants_secondary: " << ( wants_secondary ? "yes" : "no" ) << endl; + cout << "wants_two_lines: " << ( wants_two_lines ? "yes" : "no" ) << endl; + cout << "argc: " << argc << "; first_nonopt: " << first_nonopt << endl; +#endif + + if(wants_help) + { + usage(cout, argv[0]); + + cout << "You can use -one- of -a, -p, -s, or -t." << endl; + + cout << endl; + cout << endl; + cout << "OPTIONS" << endl; + cout << endl; + cout << " -a or --all" << endl; + cout << " [default] show all members, both primary and secondary." << endl; + cout << endl; + cout << " -p or --primary" << endl; + cout << " show only primary members" << endl; + cout << " -s or --secondary" << endl; + cout << " show only secondary members" << endl; + cout << "" << endl; + cout << " -t or --two-lines" << endl; + cout << " output two lines, first is primary group members," << endl; + cout << " secondary members on the second line." << endl; + cout << "" << endl; + cout << " -h or --help" << endl; + cout << " print this message and exit successfully" << endl; + cout << "" << endl; + cout << "ARGUMENTS" << endl; + cout << "" << endl; + cout << " group (required)" << endl; + cout << " the group to find members in." << endl; + cout << "" << endl; + cout << "SEE ALSO: man members for details." << endl; + cout << "" << endl; + + return 0; + } + else if(argc != first_nonopt + 1) + { + usage(cerr, argv[0]); + + return 1; + } + + if(argc == first_nonopt + 1) + { + int bad = 0; + int sbad = 0; + int printedOne; + + if // user wants default (i.e., no option selected)? + ( + ! + ( + wants_all_members || + wants_primary || + wants_secondary || + wants_two_lines + ) + ) + { + wants_all_members = 1; // the default for this app + } + + // fill in responses to errors + + if(wants_all_members) + { + printedOne = 0; + + if(display_primaries(argv[first_nonopt], &printedOne)) + { + if(display_secondaries(argv[first_nonopt], &printedOne)) + if(printedOne) + cout << endl; + else + sbad = 1; + } + else + bad = 1; + } + else if(wants_primary) + { + printedOne = 0; + + bad = ! display_primaries(argv[first_nonopt], &printedOne); + + if(! bad) + cout << endl; + } + else if(wants_secondary) + { + printedOne = 0; + + bad = ! display_secondaries(argv[first_nonopt], &printedOne); + + if( (! bad) && printedOne) + cout << endl; + } + else if(wants_two_lines) + { + printedOne = 0; + + bad = ! display_primaries(argv[first_nonopt], &printedOne); + + if(! bad) + { + cout << endl; + + printedOne = 0; + + sbad = ! display_secondaries(argv[first_nonopt], &printedOne); + + cout << endl; + } + } + + if(bad) // presumably, group does not exist + { + cerr << "members: group " << argv[first_nonopt] << " does not exist"; + cerr << endl; + } + + result = bad || sbad; + } + else + result = 1; + + return result; +} + diff --git a/members_src/members.o b/members_src/members.o Binary files differnew file mode 100644 index 0000000..ddc356b --- /dev/null +++ b/members_src/members.o diff --git a/pwauth b/pwauth new file mode 160000 +Subproject 3b4ba71005c4e5436d0f2d1638bddaa86950e81 diff --git a/user_pam.php b/user_pam.php new file mode 100644 index 0000000..7368cb5 --- /dev/null +++ b/user_pam.php @@ -0,0 +1,91 @@ +<?php +/** + * @author CSharplie + * + * @copyright Copyright (c) 2016, CSharplie + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\user_pam; + +class USER_PAM extends \OC_User_Backend implements \OCP\UserInterface { + protected $pam_bin_path; + private $user_search; + + public function __construct() { + $this->pam_bin_path = \OCP\Config::getAppValue('user_pam', 'pam_path', OC_USER_BACKEND_PAM_PATH); + $this->members_bin_path = \OCP\Config::getAppValue('user_pam', 'members_path', OC_USER_BACKEND_MEMBERS_PATH); + } + + public function implementsAction($actions) { + return (bool)((OC_USER_BACKEND_CHECK_PASSWORD | OC_USER_BACKEND_SET_PASSWORD) & $actions); + } + + private function userMatchesFilter($user) { + return (strripos($user, $this->user_search) !== false); + } + + public function deleteUser($_uid) { + return false; + } + + public function checkPassword( $uid, $password ) { + $uid = strtolower($uid); + + if (!preg_match('/[A-Za-z_]+/', $uid)) + return false; + + $userGroups = shell_exec("groups $uid"); + if (!preg_match('/' . OC_USER_BACKEND_PAM_GROUP . '/', $userGroups)) + return false; + + $handle = popen($this->pam_bin_path, 'w'); + if ($handle === false) + return false; + + if (fwrite($handle, "$uid\n$password\n") === false) + return false; + + $result = pclose( $handle ); + if (0 === $result) + return $uid; + + return false; + } + + public function setPassword( $uid, $password ) { + return false; + } + + public function userExists( $uid ){ + $user = posix_getpwnam( strtolower($uid) ); + return is_array($user); + } + + public function getUsers($search = '', $limit = 10, $offset = 10){ + $returnArray = explode(" ", shell_exec(OC_USER_BACKEND_MEMBERS_PATH . " " . OC_USER_BACKEND_PAM_GROUP)); + + $this->user_search = $search; + if(!empty($this->user_search)) + $returnArray = array_filter($returnArray, array($this, 'userMatchesFilter')); + + if($limit = -1) + $limit = null; + return array_slice($returnArray, $offset, $limit); + } +} + +?> |
