[Pkg-securepass-commits] [SCM] libnss-securepass/master: Imported Upstream version 0.4

alessio at users.alioth.debian.org alessio at users.alioth.debian.org
Fri Aug 28 16:12:37 UTC 2015


The following commit has been merged in the master branch:
commit ec56ca0c6c4ac74dd1cfcc30ab39042d77f097b8
Author: Alessio Treglia <alessio.treglia at smartodds.co.uk>
Date:   Fri Aug 28 17:10:24 2015 +0100

    Imported Upstream version 0.4

diff --git a/ADD_USERS.sh b/ADD_USERS.sh
new file mode 100644
index 0000000..202610d
--- /dev/null
+++ b/ADD_USERS.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+sp-user-add -n john -s gianelli -e john.gianelli at wiran.net -m +00000000000 jgianelli at wiran.net
+sp-user-xattrs jgianelli at wiran.net set posixuid 1001
+sp-user-xattrs jgianelli at wiran.net set posixgid 101
+sp-user-xattrs jgianelli at wiran.net set posixshell /bin/sh
+sp-user-xattrs jgianelli at wiran.net set posixhomedir /usr/home
+sp-user-xattrs jgianelli at wiran.net set posixgecos 'was a really good player'
+
+sp-user-add -n bob -s morse -e bob.morse at wiran.net -m +00000000000 bmorse at wiran.net
+sp-user-xattrs bmorse at wiran.net set posixuid 1002
+sp-user-xattrs bmorse at wiran.net set posixgid 102
+sp-user-xattrs bmorse at wiran.net set posixshell /bin/bash
+sp-user-xattrs bmorse at wiran.net set posixhomedir /home
+sp-user-xattrs bmorse at wiran.net set posixgecos 'one of my models'
+
+sp-user-add -n chuck -s jura -e chuck.jura at wiran.net -m +00000000000 cjura at wiran.net
+sp-user-xattrs cjura at wiran.net set posixuid 1003
+sp-user-xattrs cjura at wiran.net set posixgid 103
+sp-user-xattrs cjura at wiran.net set posixshell /bin/bash
+sp-user-xattrs cjura at wiran.net set posixhomedir /home
+sp-user-xattrs cjura at wiran.net set posixgecos 'great center'
+
+sp-user-add -n charlie -s yelverton -e charlie.yelverton at wiran.net -m +00000000000 cyelverton at wiran.net
+sp-user-xattrs cyelverton at wiran.net set posixuid 1004
+sp-user-xattrs cyelverton at wiran.net set posixgid 104
+sp-user-xattrs cyelverton at wiran.net set posixshell /bin/bash
+sp-user-xattrs cyelverton at wiran.net set posixhomedir /home
+sp-user-xattrs cyelverton at wiran.net set posixgecos 'best defender'
+
+sp-user-add -n dino -s meneghin -e dino.meneghin at wiran.net -m +00000000000 dmeneghin at wiran.net
+sp-user-xattrs dmeneghin at wiran.net set posixuid 1005
+sp-user-xattrs dmeneghin at wiran.net set posixgid 105
+sp-user-xattrs dmeneghin at wiran.net set posixshell /bin/bash
+sp-user-xattrs dmeneghin at wiran.net set posixhomedir /home
+sp-user-xattrs dmeneghin at wiran.net set posixgecos 'il monumento nazionale'
+
+sp-user-add -n pierluigi -s marzorati -e pierluigi.marzorati at wiran.net -m +00000000000 pmarzorati at wiran.net
+sp-user-xattrs pmarzorati at wiran.net set posixuid 1006
+sp-user-xattrs pmarzorati at wiran.net set posixgid 106
+sp-user-xattrs pmarzorati at wiran.net set posixshell /bin/bash
+sp-user-xattrs pmarzorati at wiran.net set posixhomedir /home
+sp-user-xattrs pmarzorati at wiran.net set posixgecos 'il giocatore ingegnere'
diff --git a/DEL_USERS.sh b/DEL_USERS.sh
new file mode 100644
index 0000000..fec270a
--- /dev/null
+++ b/DEL_USERS.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+sp-user-del jgianelli at wiran.net
+sp-user-del bmorse at wiran.net
+sp-user-del cjura at wiran.net
+sp-user-del cyelverton at wiran.net
+sp-user-del dmeneghin at wiran.net
+sp-user-del pmarzorati at wiran.net
diff --git a/DO_TEST.sh b/DO_TEST.sh
index 7a15644..989ef07 100644
--- a/DO_TEST.sh
+++ b/DO_TEST.sh
@@ -1,14 +1,29 @@
 #!/bin/sh
-set +v
+#set -v
 echo
-./sp_client -l
+#./sp_client -l
 echo
 getent passwd
 echo
-getent passwd 1000
-getent passwd 1001
-getent passwd 1002
-getent passwd 1003
-getent passwd 1004
-getent passwd 1005
-getent passwd 1006
+#getent passwd 1000
+#getent passwd 1001
+#getent passwd 1002
+#getent passwd 1003
+#getent passwd 1004
+#getent passwd 1005
+#getent passwd 1006
+echo
+if [ $# != 2 ]
+then
+	echo "if you want to check authentication and pwd change, pls. run: $0 OTP Password"
+else
+	echo
+	set -v
+	./sp_client -w "gp at wiran.net $2"
+	echo
+	./sp_client -t "gp at wiran.net $1$2"
+	echo
+	./pam_client -p gp
+	echo
+	./pam_client -a gp
+fi
diff --git a/GIT_ADD.sh b/GIT_ADD.sh
deleted file mode 100644
index fbe24ee..0000000
--- a/GIT_ADD.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-git add config.guess config.sub configure configure.ac DO_TEST.sh GIT_ADD.sh install-sh jsmn.c jsmn.h LICENSE LICENSE_APACHE2 LICENSE_GNUGPL LICENSE_MIT Makefile.in minGlue.h minIni.c minIni.h nss_client.c nss_sp.c nss_sp.h README.md securepass.conf.template sp_api.c sp_api.h sp_client.c test.ini test_ini.c
diff --git a/LICENSE b/LICENSE
old mode 100755
new mode 100644
diff --git a/LICENSE_APACHE2 b/LICENSE_APACHE2
old mode 100755
new mode 100644
index ee069fd..cbf8eb4
--- a/LICENSE_APACHE2
+++ b/LICENSE_APACHE2
@@ -4,16 +4,16 @@
                         http://www.apache.org/licenses/
 
 
-    EXCEPTION TO THE APACHE 2.0 LICENSE
+    EXCEPTION TO THE APACHE 2.0 LICENSE
 
-    As a special exception to the Apache License 2.0 (and referring to the
-    definitions in Section 1 of this license), you may link, statically or
+    As a special exception to the Apache License 2.0 (and referring to the
+    definitions in Section 1 of this license), you may link, statically or
     dynamically, the "Work" to other modules to produce an executable file
     containing portions of the "Work", and distribute that executable file
     in "Object" form under the terms of your choice, without any of the
-    additional requirements listed in Section 4 of the Apache License 2.0.
-    This exception applies only to redistributions in "Object" form (not
-    "Source" form) and only if no modifications have been made to the "Work".
+    additional requirements listed in Section 4 of the Apache License 2.0.
+    This exception applies only to redistributions in "Object" form (not
+    "Source" form) and only if no modifications have been made to the "Work".
 
 
    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
diff --git a/LICENSE_GNUGPL b/LICENSE_GNUGPL
old mode 100755
new mode 100644
diff --git a/LICENSE_MIT b/LICENSE_MIT
old mode 100755
new mode 100644
index 1bf1158..e38f663
--- a/LICENSE_MIT
+++ b/LICENSE_MIT
@@ -1,19 +1,19 @@
-Copyright (c) 2010 Serge A. Zaitsev
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+Copyright (c) 2010 Serge A. Zaitsev
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
\ No newline at end of file
diff --git a/Makefile.in b/Makefile.in
index 3361195..ff28923 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,35 +1,42 @@
 LIBNSS_SP=libnss_sp.so.2
 LIBNSS_SP_SO=libnss_sp.so
+LIBPAM_SP_SO=pam_sp.so
 CC=@CC@
-LDFLAGS+=-shared -Wl,-Bdynamic  -Wl,-soname,$(LIBNSS_SP) @LDFLAGS@
+LDFLAGS+=-shared -Wl,-Bdynamic @LDFLAGS@
 LIBS+=@LIBS@
 CFLAGS+=-I. -g @CPPFLAGS@ @CFLAGS@
 INSTALL=@INSTALL@
 exec_prefix=@exec_prefix@
 libdir=@libdir@
+pamlibdir=security
 
 #Set a few distribution variables
 DISTNAME=@PACKAGE_NAME at -@PACKAGE_VERSION@
 DISTDIR=${DISTNAME}
 DISTBALL=${DISTNAME}.tgz
 
+HDRS=nss_sp.h pam_sp_auth.h sp_api.h jsmn.h minIni.h minGlue.h
 LIBSP_SRCS=sp_api.c jsmn.c minIni.c
 LIBSP_OBJS=sp_api.o jsmn.o minIni.o
-SRCS=nss_sp.c
-OBJS=nss_sp.o
-HDRS=nss_sp.h sp_api.h jsmn.h minIni.h minGlue.h
+LIBNSS_SRCS=nss_sp.c
+LIBNSS_OBJS=nss_sp.o
+LIBPAM_SRCS=pam_sp.c
+LIBPAM_OBJS=pam_sp.o
 TEST_INI_SRCS=test_ini.c
 TEST_INI_OBJS=test_ini.o
 SP_CLIENT_SRCS=sp_client.c
 SP_CLIENT_OBJS=sp_client.o
+PAM_CLIENT_SRCS=pam_client.c
+PAM_CLIENT_OBJS=pam_client.o
 NSS_CLIENT_SRCS=nss_client.c
 NSS_CLIENT_OBJS=nss_client.o
 LIBSP=libsp.a
 TEST_INI=test_ini
 SP_CLIENT=sp_client
+PAM_CLIENT=pam_client
 NSS_CLIENT=nss_client
 #BASEFILES represents all *source* files, in the truest sense (no auto* artifacts)
-BASEFILES=$(LIBSP_SRCS) $(SRCS) $(HDRS) $(TEST_INI_SRCS) $(SP_CLIENT_SRCS) $(NSS_CLIENT_SRCS) \
+BASEFILES=$(LIBSP_SRCS) $(LIBNSS_SRCS) $(LIBPAM_SRCS) $(HDRS) $(TEST_INI_SRCS) $(SP_CLIENT_SRCS) $(PAM_CLIENT_SRCS) $(NSS_CLIENT_SRCS) \
           Makefile.in configure.ac install-sh config.sub config.guess \
           securepass.conf.template ADD_USERS.sh DEL_USERS.sh DO_TEST.sh test.ini \
           README.md LICENSE LICENSE_GNUGPL LICENSE_MIT LICENSE_APACHE2
@@ -40,13 +47,16 @@ DISTFILES=configure ${BASEFILES}
 %.o: %.c $(HDRS) 
 	$(CC) -c $(CFLAGS) $< -o $@
 
-all: $(LIBSP) $(LIBNSS_SP) $(TEST_INI) $(SP_CLIENT) $(NSS_CLIENT_OBJS)
+all: $(LIBSP) $(LIBNSS_SP) $(LIBPAM_SP_SO) $(TEST_INI) $(SP_CLIENT) $(NSS_CLIENT_OBJS) $(PAM_CLIENT)
  
 $(LIBSP): $(LIBSP_OBJS)
 	$(AR) -crus $(LIBSP) $(LIBSP_OBJS)
 
-$(LIBNSS_SP): $(LIBSP) $(OBJS)
-	$(CC) $(LDFLAGS) -o $(LIBNSS_SP) $(OBJS) $(LIBSP) $(LIBS)
+$(LIBNSS_SP): $(LIBSP) $(LIBNSS_OBJS)
+	$(CC) $(LDFLAGS) -Wl,-soname,$(LIBNSS_SP) -o $(LIBNSS_SP) $(LIBNSS_OBJS) $(LIBSP) $(LIBS)
+
+$(LIBPAM_SP_SO): $(LIBSP) $(LIBPAM_OBJS)
+	$(CC) $(LDFLAGS) -Wl,-soname,$(LIBPAM_SP_SO) -o $(LIBPAM_SP_SO) $(LIBPAM_OBJS) $(LIBSP) $(LIBS)
 
 $(TEST_INI): $(TEST_INI_OBJS) $(LIBSP)
 	$(CC) -o $(TEST_INI) $(TEST_INI_OBJS) $(LIBSP)
@@ -54,18 +64,24 @@ $(TEST_INI): $(TEST_INI_OBJS) $(LIBSP)
 $(NSS_CLIENT): $(NSS_CLIENT_OBJS) $(LIBSP)
 	$(CC) -o $(NSS_CLIENT) $(NSS_CLIENT_OBJS) $(LIBS) -lnss_sp -lpthread
 
+$(PAM_CLIENT): $(PAM_CLIENT_OBJS) $(LIBSP)
+	$(CC) -o $(PAM_CLIENT) $(PAM_CLIENT_OBJS) $(LIBS) -lpam -lpam_misc -lpthread
+
 $(SP_CLIENT): $(SP_CLIENT_OBJS) $(LIBSP)
 	$(CC) -o $(SP_CLIENT) $(SP_CLIENT_OBJS) $(LIBSP) $(LIBS)
 
-install: $(LIBNSS_SP)
+install: $(LIBNSS_SP) $(LIBPAM_SP_SO)
 	mkdir -p $(DESTDIR)/$(libdir)
 	$(INSTALL) -o root -g root $(LIBNSS_SP) $(DESTDIR)/$(libdir)/$(LIBNSS_SP)
 	ln -sf $(LIBNSS_SP) $(DESTDIR)/$(libdir)/$(LIBNSS_SP_SO)
+	mkdir -p $(DESTDIR)/$(libdir)/$(pamlibdir)
+	$(INSTALL) -o root -g root $(LIBPAM_SP_SO) $(DESTDIR)/$(libdir)/$(pamlibdir)/$(LIBPAM_SP_SO)
 
 clean:
-	rm -f $(LIBSP) $(LIBNSS_SP) $(TEST_INI) $(SP_CLIENT) $(NSS_CLIENT) $(LIBSP_OBJS) $(OBJS) $(TEST_INI_OBJS) \
-          $(SP_CLIENT_OBJS) $(NSS_CLIENT_OBJS)
-	rm -f $(libdir)/$(LIBNSS_SP) $(libdir)/$(LIBNSS_SP_SO)
+	rm -f $(LIBSP) $(LIBNSS_SP) $(LIBPAM_SP_SO) $(TEST_INI) $(SP_CLIENT) $(NSS_CLIENT) $(PAM_CLIENT) $(LIBSP_OBJS) \
+          $(LIBNSS_OBJS) $(LIBPAM_OBJS) $(TEST_INI_OBJS) $(SP_CLIENT_OBJS) $(NSS_CLIENT_OBJS) $(PAM_CLIENT_OBJS)
+	#rm -f $(libdir)/$(LIBNSS_SP) $(libdir)/$(LIBNSS_SP_SO)
+	#rm -f $(libdir)/$(pamlibdir)/$(LIBPAM_SP_SO)
 	rm -f ${DISTDIR}/* ${DISTBALL}
 
 distclean: clean
diff --git a/README.md b/README.md
index 8385526..7804817 100644
--- a/README.md
+++ b/README.md
@@ -1,21 +1,58 @@
-NSS (Name Service Switch) module for SecurePass
-===============================================
+# NSS and PAM modules for SecurePass
 
-This is a NSS module for users defined on SecurePass.
+This repository contains an NSS module and a PAM module for users defined on SecurePass.
 SecurePass provides web single sign-on through the CAS protocol.
 
 More on SecurePass at http://www.secure-pass.net
 
-To install and configure the module:
+To install and configure the modules:
 - Install libcurl development package (e.g. libcurl4-gnutls-dev under Ubuntu)
+- Install libpam development package (e.g. libpam0g-dev under Ubuntu)
 - ./configure
 - make
 - make install
 - Copy file securepass.conf.template into /etc/securepass.conf (uid=root, gid=root, perms=600)
 - See the instructions into the file to configure the module
 - Edit file /etc/nssswitch.conf and add service 'sp' to the passwd line (e.g. 'passwd: compat sp')
-- (optional) start nscd (Name Service Cache Daemon)
+- (recommended) start nscd (Name Service Cache Daemon)
+- Configure PAM module (/lib/security/pam_sp_auth.so) under /etc/pam.d 
+- This repo includes the following sample programs to test the SecurePass, NSS and PAM APIs: 
+      sp_client, nss_client, pam_client
 
-Author
-===========================================
-gplll1818 at gmail.com, Oct 2014
+**Note**: Due to the current limitations, be aware that the parameter `endpoint` must be set to https://beta.secure-pass.net/ in /etc/securepass.conf.
+
+## NSS module
+
+There are reserved words in SecurePass extended attributes:
+
+* `posixuid` -> UID of the user
+* `posixgid` -> GID of the user
+* `posixhomedir` -> Home directory
+* `posixshell` -> Desired shell
+* `posixgecos` -> Gecos (defaults to username)
+
+`posixuid` is the only required extended attribute, this is needed to recognize a SecurePass user as a Unix user. For any other parameter, you need to set defaults in /etc/securepass.conf:
+
+```
+[nss]
+realm = domain.com
+default_gid = 100
+default_home = "/home"
+default_shell = "/bin/bash"
+```
+
+## PAM module
+
+The PAM module works both for **authentication** and for **password changing**.
+In order to be able to change your password with the PAM module, the API key must be read-write.
+Read-only API keys will result in an error.
+
+An example of PAM configuration under /etc/pam.d/:
+
+```
+password   required   /lib/security/pam_sp.so
+auth       required   /lib/security/pam_sp.so
+```
+
+# Author
+gplll1818 at gmail.com, Oct 2014 - Aug 2015
diff --git a/_service b/_service
new file mode 100644
index 0000000..b1adf23
--- /dev/null
+++ b/_service
@@ -0,0 +1,12 @@
+<services>
+  <service name="tar_scm">
+    <param name="scm">git</param>
+    <param name="url">git://github.com/garlsecurity/nss_securepass.git</param>
+    <param name="revision">c1bf10da1873bc212caa857653bef0b1e899703a</param>
+    <param name="filename">nss_securepass</param>
+  </service>
+  <service name="recompress">
+    <param name="file">*.tar</param>
+    <param name="compression">gz</param>
+  </service>
+</services>
diff --git a/configure b/configure
index ac8bd9a..15db622 100755
--- a/configure
+++ b/configure
@@ -3507,6 +3507,22 @@ else
 fi
 
 
+ac_fn_c_check_header_mongrel "$LINENO" "security/pam_appl.h" "ac_cv_header_security_pam_appl_h" "$ac_includes_default"
+if test "x$ac_cv_header_security_pam_appl_h" = xyes; then :
+
+else
+  as_fn_error $? "could not locate <security/pam_appl.h> - pls. install PAM devel package" "$LINENO" 5
+fi
+
+
+ac_fn_c_check_header_mongrel "$LINENO" "security/pam_modules.h" "ac_cv_header_security_pam_modules_h" "$ac_includes_default"
+if test "x$ac_cv_header_security_pam_modules_h" = xyes; then :
+
+else
+  as_fn_error $? "could not locate <security/pam_modules.h - pls. install PAM devel package>" "$LINENO" 5
+fi
+
+
 
 # Checks for typedefs, structures, and compiler characteristics.
 ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
@@ -3569,6 +3585,100 @@ else
   as_fn_error $? "libcurl development package  is missing - pls install it" "$LINENO" 5
 fi
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pam_start in -lpam" >&5
+$as_echo_n "checking for pam_start in -lpam... " >&6; }
+if ${ac_cv_lib_pam_pam_start+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpam  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* 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 pam_start ();
+int
+main ()
+{
+return pam_start ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_pam_pam_start=yes
+else
+  ac_cv_lib_pam_pam_start=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pam_pam_start" >&5
+$as_echo "$ac_cv_lib_pam_pam_start" >&6; }
+if test "x$ac_cv_lib_pam_pam_start" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBPAM 1
+_ACEOF
+
+  LIBS="-lpam $LIBS"
+
+else
+  as_fn_error $? "PAM development package  is missing - pls install it" "$LINENO" 5
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for misc_conv in -lpam_misc" >&5
+$as_echo_n "checking for misc_conv in -lpam_misc... " >&6; }
+if ${ac_cv_lib_pam_misc_misc_conv+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpam_misc  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* 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 misc_conv ();
+int
+main ()
+{
+return misc_conv ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_pam_misc_misc_conv=yes
+else
+  ac_cv_lib_pam_misc_misc_conv=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pam_misc_misc_conv" >&5
+$as_echo "$ac_cv_lib_pam_misc_misc_conv" >&6; }
+if test "x$ac_cv_lib_pam_misc_misc_conv" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBPAM_MISC 1
+_ACEOF
+
+  LIBS="-lpam_misc $LIBS"
+
+else
+  as_fn_error $? "pam_misc library is missing - pls install it" "$LINENO" 5
+fi
+
 
 # check for gcc and set FLAGS
 if test "$ac_cv_c_compiler_gnu" = "yes"
@@ -3576,6 +3686,13 @@ then CFLAGS="$CFLAGS -Wall -fPIC"
 else as_fn_error $? "gcc is required - pls. install it" "$LINENO" 5
 fi
 
+# check for existance of directory /lib/security
+#libsecurity=/lib/security
+#if [ ! test -d $libsecurity ]; then
+#AC_MSG_ERROR(directory $libsecurity is missing)
+#fi
+
+#AC_CHECK_FILE (/lib/security,, AC_MSG_ERROR(directory /lib/security is missing))
 ac_config_files="$ac_config_files Makefile"
 
 
diff --git a/configure.ac b/configure.ac
index b6ac64a..cf9e44b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -26,12 +26,16 @@ AC_CHECK_HEADER(netdb.h,, AC_MSG_ERROR(could not locate <netdb.h>))
 AC_CHECK_HEADER(nss.h,, AC_MSG_ERROR(could not locate <nss.h>))
 AC_CHECK_HEADER(pthread.h,, AC_MSG_ERROR(could not locate <pthread.h>))
 AC_CHECK_HEADER(syslog.h,, AC_MSG_ERROR(could not locate <syslog.h>))
+AC_CHECK_HEADER(security/pam_appl.h,, AC_MSG_ERROR(could not locate <security/pam_appl.h> - pls. install PAM devel package))
+AC_CHECK_HEADER(security/pam_modules.h,, AC_MSG_ERROR(could not locate <security/pam_modules.h - pls. install PAM devel package>))
 
 # Checks for typedefs, structures, and compiler characteristics.
 AC_TYPE_SIZE_T
 
 # Checks for library functions.
 AC_CHECK_LIB(curl, curl_easy_init,,AC_MSG_ERROR(libcurl development package  is missing - pls install it))
+AC_CHECK_LIB(pam, pam_start,,AC_MSG_ERROR(PAM development package  is missing - pls install it))
+AC_CHECK_LIB(pam_misc, misc_conv,,AC_MSG_ERROR(pam_misc library is missing - pls install it))
 
 # check for gcc and set FLAGS
 if test "$ac_cv_prog_gcc" = "yes"
@@ -39,6 +43,13 @@ then CFLAGS="$CFLAGS -Wall -fPIC"
 else AC_MSG_ERROR(gcc is required - pls. install it) 
 fi
 
+# check for existance of directory /lib/security
+#libsecurity=/lib/security
+#if [ ! test -d $libsecurity ]; then
+#AC_MSG_ERROR(directory $libsecurity is missing)
+#fi
+
+#AC_CHECK_FILE (/lib/security,, AC_MSG_ERROR(directory /lib/security is missing))
 AC_CONFIG_FILES([Makefile])
 
 AC_OUTPUT
diff --git a/nss-securepass.spec b/nss-securepass.spec
new file mode 100644
index 0000000..07cf921
--- /dev/null
+++ b/nss-securepass.spec
@@ -0,0 +1,86 @@
+Summary: NSS library for SecurePass
+Name: nss-securepass
+Version: 0.3
+Release: 2%{?dist}
+Source0: https://github.com/garlsecurity/nss_securepass/archive/v%{version}/nss_securepass-v%{version}.tar.gz
+URL: https://github.com/garlsecurity/nss_securepass
+License: GPLv2+
+BuildRequires: libcurl-devel
+BuildRequires: pam-devel
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
+
+%description
+NSS (Name Service Switch) module for SecurePass
+
+SecurePass provides identity management and web single sign-on.
+
+%prep
+%setup -qn nss_securepass-%{version}
+sed -i 's|-o root -g root||g' Makefile.in
+
+%build
+%configure
+make  %{?_smp_mflags}
+
+%install
+rm -Rf %{buildroot}
+make install DESTDIR=%{buildroot} INSTALL="install -p"
+mkdir -p %{buildroot}/%{_sysconfdir}
+install -m 644 securepass.conf.template %{buildroot}/etc/securepass.conf
+
+%clean
+[ "%{buildroot}" != "/" ] && rm -rf %{buildroot}
+
+%files
+%defattr(-,root,root)
+%{_libdir}/*.so*
+%{_libdir}/security/*.so*
+%attr(0600,root,root) %config(noreplace) /etc/securepass.conf
+%doc README.md
+%doc securepass.conf.template
+
+%if 0%{?rhel} <= 6
+   %doc LICENSE LICENSE_APACHE2 LICENSE_GNUGPL LICENSE_MIT
+%else 
+   %license LICENSE LICENSE_APACHE2 LICENSE_GNUGPL LICENSE_MIT
+%endif
+
+%post -p /sbin/ldconfig
+%postun -p /sbin/ldconfig
+
+%changelog
+* Sun Aug 16 2015 Giuseppe Paterno' <gpaterno at gpaterno.com> 0.3-2
+- Fixes in buildrequires
+
+* Tue Aug 11 2015 Giuseppe Paterno' <gpaterno at gpaterno.com> 0.3-1
+- Updated 0.3 to have PAM
+
+* Wed Feb 11 2015 Giuseppe Paterno' <gpaterno at gpaterno.com> 0.2.2-1
+- Sync'ed SPEC with upstream
+
+* Tue Feb 10 2015 Giuseppe Paterno' <gpaterno at gpaterno.com> 0.2-5
+- Changed to tags in RPM, following now tags upstream
+- More fixes coming from bug #1162234
+
+* Wed Feb 4 2015 Giuseppe Paterno' <gpaterno at gpaterno.com> 0.2-4
+- Converted licenses to Unix format
+- Modified spec to comply with Fedora rules
+
+* Wed Feb 4 2015 Marina Latini <deneb_alpha at opensuse.org> 0.2-4
+- Fixed LICENSES files permissions
+- Fixed license identifier accordig to https://spdx.org/licenses/
+- Fixed spec name
+- Added _service file for auto download (Suse OBS)
+
+* Thu Jan 29 2015 Giuseppe Paterno' <gpaterno at gpaterno.com> 0.2-3
+- More changes to the SPEC for bug #1162234
+
+* Wed Jan 28 2015 Giuseppe Paterno' <gpaterno at garl.ch> 0.2-2
+- Fixed SPEC files for bug #1162234
+
+* Fri Nov 14 2014 Giuseppe Paterno' <gpaterno at garl.ch> 0.2-1
+- Fixed lookup from UID
+- Changed buildroot variable to macro
+ 
+* Fri Nov 7 2014 Giuseppe Paterno' <gpaterno at garl.ch> 0.1-1
+- First RPM of the SecurePass NSS module
diff --git a/nss_sp.c b/nss_sp.c
index eb141e6..7e889c0 100644
--- a/nss_sp.c
+++ b/nss_sp.c
@@ -92,6 +92,11 @@ enum nss_status _nss_sp_getpwnam_r (const char *name,
 		/* Fill the output fields */
 		int pos = 0;
 
+		if ((xattrs->posixuid[0] == 0) || (xattrs->posixgid[0] == 0)) {
+			/* uid or gid are not defined - not a valid posix user */
+			free (xattrs);
+			return NSS_STATUS_NOTFOUND;
+		}
 		result->pw_uid = strtoul(xattrs->posixuid, NULL, 10);
 
 		result->pw_gid = strtoul(xattrs->posixgid, NULL, 10);
@@ -157,16 +162,27 @@ enum nss_status _nss_sp_getpwent_r (struct passwd *result,
 		}		
 	}
 
-	/* Call SP API to get next entry */
-	if (u_idx == u_len) {
-		/* reached end of list */
-		_nss_sp_leave ();
-		return NSS_STATUS_NOTFOUND;
+	/* We loop because we need to discard users where uid or gid is not defined */
+	char *s;
+	enum nss_status rc;
+	while (1) {
+		/* Call SP API to get next entry */
+		if (u_idx == u_len) {
+			/* reached end of list */
+			_nss_sp_leave ();
+			return NSS_STATUS_NOTFOUND;
+		}
+		s = *(user_list + u_idx);
+		rc = _nss_sp_getpwnam_r (strtok (s, "@"), result, buffer, buflen, errnop);
+		*(s + strlen(s)) = '@';	
+		u_idx++;
+		if (rc == NSS_STATUS_SUCCESS) {
+			break;
+		} else {
+			/* get next user */
+			continue;
+		}
 	}
-	char *s = *(user_list + u_idx);
-	enum nss_status rc = _nss_sp_getpwnam_r (strtok (s, "@"), result, buffer, buflen, errnop);
-	*(s + strlen(s)) = '@';	
-	u_idx++;
 	_nss_sp_leave ();
 	return rc;
 }
@@ -202,6 +218,10 @@ enum nss_status _nss_sp_getpwuid_r (uid_t uid,
 		rc = _nss_sp_getpwnam_r (strtok (s, "@"), result, buffer, buflen, errnop);
 		*(s + strlen(s)) = '@';	
 		if (rc != NSS_STATUS_SUCCESS) {
+			if (i < (u_len - 1)) {
+				/* not at the end of the list - get next user */
+				continue;
+			}
 			_nss_sp_leave ();
 			return rc;
 		}	
diff --git a/pam_client.c b/pam_client.c
new file mode 100644
index 0000000..44b726c
--- /dev/null
+++ b/pam_client.c
@@ -0,0 +1,126 @@
+/*
+ *
+ * Author: gplll <gplll1818 at gmail.com>, Aug 2015
+ *  
+ * This program provides a minimal test of SecurePass PAM
+ * To run the program, create file /etc/pam.s/pam_sp_client, wtih the following contents:
+ * password   required   /lib/security/pam_sp.so debug
+ * auth       required   /lib/security/pam_sp.so debug
+ */
+
+#include <security/pam_appl.h>
+#include <security/pam_misc.h>
+#include <stdio.h>
+
+static struct pam_conv conv = {
+misc_conv,
+NULL
+};
+
+char *user_pwd = NULL;
+int setpwd = 0;
+int authuser = 0;
+
+void usage (char *program) {
+	fprintf(stderr, "Usage: %s [-p user] [-a user] [-h]\n", program);
+	fprintf(stderr, "       -p: set user password\n");
+	fprintf(stderr, "       -a: authenticate user\n");
+	fprintf(stderr, "       -h: display usage\n");
+	fprintf(stderr, "either -p or -a options must be specified\n");
+}
+
+void get_options (int argc, char *argv[]) 
+{
+	int opt;
+	while ((opt = getopt(argc, argv, "hp:a:")) != -1) {
+		switch (opt) {
+			case 'p':
+				setpwd = 1;
+				user_pwd = optarg;
+			break;
+			case 'a':
+				authuser = 1;
+				user_pwd = optarg;
+			break;
+			case 'h':
+			default: /* '?' */
+				usage (argv[0]);
+				exit(0);
+		}
+	}
+	if ((setpwd == 0) && (authuser == 0)) {
+		usage (argv[0]);
+		exit (0);
+	}
+}
+
+void set_pwd (char * user) {
+	pam_handle_t *pamh=NULL;
+	int retval = pam_start("pam_sp_client", user, &conv, &pamh);
+
+	if (retval == PAM_SUCCESS) {
+		retval = pam_chauthtok(pamh, 0);
+	} else {
+		printf ("pam_start() returned error\n");
+		return;
+	}
+
+	if (retval == PAM_SUCCESS) {
+		printf ("password for user %s has been set\n", user);
+	} else {
+		printf ("pam_chauthtok() returned error %d for user %s\n", retval, user);
+    }
+	if (pam_end(pamh, retval) != PAM_SUCCESS) { 
+		printf ("pam_end() returned error for user %s\n", user);
+		return;
+	}
+}
+
+void auth_user (char * user) {
+	pam_handle_t *pamh=NULL;
+	int retval = pam_start("pam_sp_client", user, &conv, &pamh);
+
+	if (retval == PAM_SUCCESS) {
+		retval = pam_authenticate(pamh, 0);
+	} else {
+		printf ("pam_start() returned error\n");
+		return;
+	}
+
+	if (retval == PAM_SUCCESS) {
+		printf ("user %s has been authenticated\n", user);
+	} else {
+		printf ("pam_authenticate() returned error %d for user %s\n", retval, user);
+    }
+	if (pam_end(pamh, retval) != PAM_SUCCESS) { 
+		printf ("pam_end() returned error for user %s\n", user);
+		return;
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	get_options (argc, argv);
+	char *user = user_pwd;
+	if (setpwd) {
+		set_pwd (user);
+	}
+	if (authuser) {
+		auth_user (user);
+	}
+#if 0
+	user = strtok (user_pwd, " ");
+	pwd = strtok (NULL, " ");
+	if ((user == NULL) || (pwd == NULL)) {
+		printf ("argument must be in the form 'user pwd'\n");
+	}
+	if (setpwd) {
+		set_pwd (user, pwd);
+	}
+	if (authuser) {
+		auth_user (user, pwd);
+	}
+#endif
+	return 1;
+}
+
diff --git a/pam_sp.c b/pam_sp.c
new file mode 100644
index 0000000..ca76519
--- /dev/null
+++ b/pam_sp.c
@@ -0,0 +1,426 @@
+/*
+ *
+ * File: 	pam_sp.c
+ * Author: 	gplll <gplll1818 at gmail.com>, Aug 2015
+ *
+ *   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; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ * This program is derivative work of pam_radius.c code, 
+ * taken from https://github.com/FreeRADIUS/pam_radius 
+ *
+ * pam_radius.c License NOTICE:
+ *   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; either version 2 of the License, 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
+ *
+ * The original pam_radius.c code is copyright (c) Cristian Gafton, 1996,
+ *                                             <gafton at redhat.com>
+ *
+ * Some challenge-response code is copyright (c) CRYPTOCard Inc, 1998.
+ *                                              All rights reserved.
+ */
+
+#define PAM_SM_AUTH
+#define PAM_SM_PASSWORD
+#define PAM_SM_SESSION
+#define PAM_SM_ACCOUNT
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <string.h>
+#include <pthread.h>
+#include <syslog.h>
+#include <errno.h>
+
+#include <security/pam_appl.h>
+#include <security/pam_modules.h>
+#include "sp_api.h"
+#include "pam_sp.h"
+
+#define SP_INIT \
+    if ((sp_config.status != SP_INITED)) { \
+        if (!(sp_init ())) return PAM_SERVICE_ERR; \
+    }
+
+#undef PAM_FAIL_CHECK
+#define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) { return retval; }
+
+/* argument parsing */
+/* returns PAM_SUCCESS if success, PAM_SERVICE_ERR if error */
+static int _pam_parse(int argc, const char **argv, sp_conf_t *conf)
+{
+
+	memset(conf, 0, sizeof(sp_conf_t)); /* ensure it's initialized */
+
+	/*
+	 *	If either is not there, then we can't parse anything.
+	 */
+	if ((argc == 0) || (argv == NULL)) {
+		return PAM_SUCCESS;
+	}
+
+	/* step through arguments */
+	for (; argc-- > 0; ++argv) {
+		if (!strcmp(*argv, "debug")) {
+			conf->debug = 1;
+		} 
+		else if  (!strcmp(*argv, "debug_stderr")) {
+            conf->debug_stderr = 1;
+        }
+		else {
+			error ("unrecognized option: %s", *argv);
+			return PAM_SERVICE_ERR; 
+		}
+	}
+	return PAM_SUCCESS;
+}
+
+/* Callback function used to free the saved return value for pam_setcred. */
+void _int_free(pam_handle_t * pamh, void *x, int error_status)
+{
+		free(x);
+}
+
+static int sp_converse(pam_handle_t *pamh, int msg_style, char *message, char **password)
+{
+	const struct pam_conv *conv;
+	struct pam_message resp_msg;
+	const struct pam_message *msg[1];
+	struct pam_response *resp = NULL;
+	int retval;
+
+	resp_msg.msg_style = msg_style;
+	resp_msg.msg = message;
+	msg[0] = &resp_msg;
+
+	/* grab the password */
+	retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
+	PAM_FAIL_CHECK;
+
+	retval = conv->conv(1, msg, &resp,conv->appdata_ptr);
+	PAM_FAIL_CHECK;
+
+	if (password) {		/* assume msg.type needs a response */
+		/* I'm not sure if this next bit is necessary on Linux */
+
+		*password = resp->resp;
+		free(resp);
+	}
+
+	return PAM_SUCCESS;
+}
+
+#undef PAM_FAIL_CHECK
+#define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) { \
+	int *pret = malloc(sizeof(int)); \
+	*pret = retval;	\
+	pam_set_data(pamh, "sp_setcred_return", (void *) pret, _int_free);	\
+	return retval; }
+
+PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc,const char **argv)
+{
+	const char *user;
+	char *password = NULL;
+	int retval;
+	sp_conf_t config;
+
+	retval = _pam_parse(argc, argv, &config);
+	PAM_FAIL_CHECK;
+	SP_INIT;
+	debug (2, "==> pam_sm_authenticate");
+
+	/* grab the user name */
+	retval = pam_get_user(pamh, &user, NULL);
+	PAM_FAIL_CHECK;
+
+	/* check that they've entered something */
+	if (user == NULL)  {
+		retval = PAM_USER_UNKNOWN;
+		PAM_FAIL_CHECK;
+	}
+	debug (2, "Got user name %s", user);
+
+#undef PAM_FAIL_CHECK
+#define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) {goto error; }
+
+	/* grab the password (if any) from the previous authentication layer */
+	retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) &password);
+	PAM_FAIL_CHECK;
+
+	if (password) {
+		password = strdup(password);
+		/* debug (2, "Got password from PAM: %s", password); */
+		debug (2, "Got password from PAM");
+	}
+
+	/* no previous password: get one from the user */
+	if (!password) {
+		retval = sp_converse(pamh, PAM_PROMPT_ECHO_OFF, "Password: ", &password);
+		PAM_FAIL_CHECK;
+	} 
+
+	/* call securepass API */
+	if (sp_user_auth_p (user, password) != -1) {
+		retval = PAM_SUCCESS;
+	} else {
+		retval = PAM_AUTH_ERR;	/* authentication failure */
+
+error:
+		/* If there was a password pass it to the next layer */
+		if (password && *password) {
+			pam_set_item(pamh, PAM_AUTHTOK, password);
+		}
+	}
+
+	debug (2, "authentication for user %s %s", user, retval==PAM_SUCCESS ? "succeeded":"failed");
+
+	_pam_forget(password);
+
+	int *pret = malloc(sizeof(int));
+	*pret = retval;
+	pam_set_data(pamh, "sp_setcred_return", (void *) pret, _int_free);
+
+	return retval;
+}
+
+#undef PAM_FAIL_CHECK
+#define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) { return retval; }
+
+PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc,const char **argv)
+{
+	int retval, *pret;
+	sp_conf_t config;
+
+	retval = _pam_parse(argc, argv, &config);
+	PAM_FAIL_CHECK;
+	SP_INIT;
+	debug (2, "==> pam_sm_setcred, flags=0x%x argc=%d", flags, argc);
+
+	retval = PAM_SUCCESS;
+	pret = &retval;
+	pam_get_data(pamh, "sp_setcred_return", (const void **) &pret);
+	return (*pret==PAM_SUCCESS ? PAM_SUCCESS : PAM_CRED_ERR);
+}
+
+PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+	sp_conf_t config;
+
+	int retval = _pam_parse(argc, argv, &config);
+	PAM_FAIL_CHECK;
+	SP_INIT;
+	debug (2, "==> pam_sm_open_session() called...returning PAM_SUCCESS");
+	return PAM_SUCCESS;
+}
+
+PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+	sp_conf_t config;
+
+	int retval = _pam_parse(argc, argv, &config);
+	PAM_FAIL_CHECK;
+	SP_INIT;
+	debug (2, "==> pam_sm_close_session() called...returning PAM_SUCCESS");
+	return PAM_SUCCESS;
+}
+
+
+PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+	const char *user;
+	char *password = NULL;
+	char *new_password = NULL;
+	char *check_password = NULL;
+	int retval = PAM_AUTHTOK_ERR;
+	int attempts;
+	sp_conf_t config;
+
+	retval = _pam_parse(argc, argv, &config);
+	PAM_FAIL_CHECK;
+	SP_INIT;
+	debug (2, "==> pam_sm_chauthtok, flags=0x%x argc=%d", flags, argc);
+	/*
+     * check args, only accept debug, otherwise return error
+     */
+	/* grab the user name */
+	retval = pam_get_user(pamh, &user, NULL);
+	PAM_FAIL_CHECK;
+
+	/* check that they've entered something */
+	if (user == NULL) {
+		return PAM_USER_UNKNOWN;
+	}
+	debug (2, "user=%s", user);
+
+#undef PAM_FAIL_CHECK
+#define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) {goto error; }
+
+	/* grab the old password (if any) from the previous password layer */
+	retval = pam_get_item(pamh, PAM_OLDAUTHTOK, (const void **) &password);
+	PAM_FAIL_CHECK;
+	if (password) {
+		password = strdup(password);
+		/* debug (2, "old pwd= %s", password); */
+		debug (2, "got old pwd from previous layer");
+	}
+
+	/* grab the new password (if any) from the previous password layer */
+	retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) &new_password);
+	PAM_FAIL_CHECK;
+	if (new_password) {
+		new_password = strdup(new_password);
+		/* debug (2, "new pwd= %s", new_password); */
+		debug (2, "got new pwd from previous layer");
+	}
+
+	/* preliminary password change checks. */
+	if (flags & PAM_PRELIM_CHECK) {
+		if (!password) {		/* no previous password: ask for one */
+			retval = sp_converse(pamh, PAM_PROMPT_ECHO_OFF, "Securepass password: ", &password);
+			PAM_FAIL_CHECK;
+		}
+		/*
+		 * We now check the password to see if it's the right one.
+		 * If it isn't, we let the user try again.
+		 */
+
+		/* call securepass API */
+ 		if (sp_user_auth_p (user, password) == -1) {
+            debug (4, "old password for user %s is wrong\n", user);
+			_pam_forget(password);
+			retval = PAM_PERM_DENIED;
+			goto error;
+        } else {
+            debug (4, "old password for user %s is correct\n", user);
+        }
+
+		/*
+		 * We're now sure it's the right user.
+		 * Ask for their new password, if appropriate
+		 */
+
+		if (!new_password) {	/* not found yet: ask for it */
+			int new_attempts;
+			attempts = 0;
+
+			/* loop, trying to get matching new passwords */
+			while (attempts++ < 3) {
+
+				/* loop, trying to get a new password */
+				new_attempts = 0;
+				while (new_attempts++ < 3) {
+					retval = sp_converse (pamh, PAM_PROMPT_ECHO_OFF,
+							"New password: ", &new_password);
+					PAM_FAIL_CHECK;
+
+					if (strcmp(password, new_password) == 0) { /* are they the same? */
+						sp_converse(pamh, PAM_ERROR_MSG,
+						 "You must choose a new password.", NULL);
+						_pam_forget(new_password);
+						continue;
+					}
+
+					break;		/* the new password is OK */
+				}
+
+				if (new_attempts >= 3) { /* too many new password attempts: die */
+					retval = PAM_AUTHTOK_ERR;
+					goto error;
+				}
+
+				/* make sure of the password by asking for verification */
+				retval = sp_converse(pamh, PAM_PROMPT_ECHO_OFF,
+						      "New password (again): ", &check_password);
+				PAM_FAIL_CHECK;
+
+				retval = strcmp(new_password, check_password);
+				_pam_forget(check_password);
+
+				/* if they don't match, don't pass them to the next module */
+				if (retval != 0) {
+					_pam_forget(new_password);
+					sp_converse(pamh, PAM_ERROR_MSG,
+								 "You must enter the same password twice.", NULL);
+					retval = PAM_AUTHTOK_ERR;
+					goto error;		/* ??? maybe this should be a 'continue' ??? */
+				}
+
+				break;			/* everything's fine */
+			}	/* loop, trying to get matching new passwords */
+
+			if (attempts >= 3) { /* too many new password attempts: die */
+				retval = PAM_AUTHTOK_ERR;
+				goto error;
+			}
+		} /* now we have a new password which passes all of our tests */
+
+	} else if (flags & PAM_UPDATE_AUTHTOK) {
+
+		if (!password || !new_password) { /* ensure we've got passwords */
+			retval = PAM_AUTHTOK_ERR;
+			goto error;
+		}
+
+		/* call SP API to change the passwd */
+ 		if (sp_user_password_change_p (user, new_password) == -1) {
+            debug (4, "can't set new password for user %s\n", user);
+			retval = PAM_AUTHTOK_ERR;
+			goto error;
+        } else {
+            debug (4, "new password for user %s has been set\n", user);
+        }
+	}
+
+	/*
+	 * Send the passwords to the next stage if preliminary checks fail,
+	 * or if the password change request fails.
+	 */
+	if ((flags & PAM_PRELIM_CHECK) || (retval != PAM_SUCCESS)) {
+	error:
+
+		/* If there was a password pass it to the next layer */
+		if (password && *password) {
+			pam_set_item(pamh, PAM_OLDAUTHTOK, password);
+		}
+
+		if (new_password && *new_password) {
+			pam_set_item(pamh, PAM_AUTHTOK, new_password);
+		}
+	}
+
+	debug (2, "password change %s", retval==PAM_SUCCESS ? "succeeded" : "failed");
+
+	_pam_forget(password);
+	_pam_forget(new_password);
+	return retval;
+}
+
+#undef PAM_FAIL_CHECK
+#define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) { return retval; }
+
+PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh,int flags,int argc,const char **argv)
+{
+	sp_conf_t config;
+
+	int retval = _pam_parse(argc, argv, &config);
+	PAM_FAIL_CHECK;
+	SP_INIT;
+	debug (2, "==> pam_sm_acct_mgmt() called...returning PAM_SUCCESS");
+	return PAM_SUCCESS;
+}
diff --git a/pam_sp.h b/pam_sp.h
new file mode 100644
index 0000000..28eb3ad
--- /dev/null
+++ b/pam_sp.h
@@ -0,0 +1,34 @@
+/*
+ * File: 	pam_sp.h
+ * Author: 	gplll <gplll1818 at gmail.com>, Aug 2015
+ *
+ *   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; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ * This program is derivative work of pam_radius.c code, 
+ * taken from https://github.com/FreeRADIUS/pam_radius 
+ */
+
+typedef struct sp_conf_t {
+	int debug;
+	int debug_stderr;
+} sp_conf_t;
+
+#define PAM_DEBUG			1
+#define PAM_DEBUG_STDERR	2
+
+#define error(fmt, args...) \
+syslog(LOG_AUTHPRIV|LOG_ERR, "pam_sp: thread %u - error: " fmt, (uint)pthread_self() , ## args); \
+
+#define debug(level, fmt, args...) \
+if ((config.debug)) { \
+syslog(LOG_AUTHPRIV|LOG_DEBUG, "pam_sp: thread %u - " fmt, (uint)pthread_self() , ## args); \
+} \
+else if ((config.debug_stderr)) { \
+fprintf(stderr, "pam_sp: " fmt "\n" , ## args); \
+}
+
+#define _pam_forget(X) if (X) {memset(X, 0, strlen(X));free(X);X = NULL;}
+
diff --git a/sp_api.c b/sp_api.c
index 028002e..779bbd9 100644
--- a/sp_api.c
+++ b/sp_api.c
@@ -22,9 +22,8 @@
 #define MANDATORY_PARAMS_NUMBER 6 /* number of mandatory params to be read from config file */
 #define ACCEPT "Accept: application/json"
 #define CONTENT_TYPE "Content-type: SecurePass CLI"
-#define DEFAULT_GID "100"
-#define DEFAULT_HOME_DIR "/home/"
-#define DEFAULT_SHELL "/bin/bash"
+#define DEFAULT_HOME "/home/"
+#define DEFAULT_SHELL "/bin/false"
 
 #define IS_ARRAY(t) ((t).type == JSMN_ARRAY)
 #define IS_OBJECT(t) ((t).type == JSMN_OBJECT)
@@ -32,7 +31,7 @@
      ((strlen(s) == (t).end - (t).start) \
      && (strncmp(js+(t).start, s, (t).end - (t).start) == 0))
      
-struct sp_config sp_config ={SP_NOT_INITED, 0, 0, "", "", "", "", "", "", ""};
+struct sp_config sp_config ={SP_NOT_INITED, 0, 0, "", "", "", "", "", "", "", "", DEFAULT_HOME, DEFAULT_SHELL, ""};
 
 struct MemoryStruct {
   char *memory;
@@ -49,7 +48,7 @@ char *types[] = {"PRIMITIVE", "OBJECT","ARRAY","STRING"};
  * result: output variable - is mallocated() within this function
  * prefix: string to be prefixed to value
  * postfix: string to be postfixed to value 
- * mandatory: param is mandadotory, increment param_count global variable
+ * mandatory: param is mandatory, increment param_count global variable
  */
 void check_ini_string (const char *key, const char *value, const char *key_to_chk, char **result, 
 						char *prefix, char *postfix, int mandatory) {
@@ -102,6 +101,8 @@ int IniCallback(const char *section, const char *key, const char *value, const v
 		check_ini_string (k, value, "endpoint", &sp_config.URL_u_list, NULL, "/api/v1/users/list", 1);
 		check_ini_string (k, value, "endpoint", &sp_config.URL_u_info, NULL, "/api/v1/users/info", 1);
 		check_ini_string (k, value, "endpoint", &sp_config.URL_u_x_list, NULL, "/api/v1/users/xattrs/list", 1);
+		check_ini_string (k, value, "endpoint", &sp_config.URL_u_pwd_chg, NULL, "/api/v1/users/password/change", 1);
+		check_ini_string (k, value, "endpoint", &sp_config.URL_u_auth, NULL, "/api/v1/users/auth", 1);
 		if (strcmp (k, "debug") == 0) {
 			sp_config.debug = atoi (value);
 		}
@@ -115,7 +116,7 @@ int IniCallback(const char *section, const char *key, const char *value, const v
 		check_ini_string (k, value, "default_home", &sp_config.default_home, NULL, "/", 0);
 		check_ini_string (k, value, "default_shell", &sp_config.default_shell, NULL, NULL, 0);
 	}
-  return 1;
+	return 1;
 }
 
 int sp_init() {
@@ -400,7 +401,9 @@ static int do_curl (const char *url, char *post_data, jsmntok_t **tok, struct Me
  * xattrs: pointer to a sp_xattrs_t that will be allocated by this function
  * caller will free() the structure after use
  * username: specifies the username in SecurePass format, i.e. user at realm
- * get_defaults: when no value, get default values from config file
+ * get_defaults: 
+ *   if 1: when no value is returned by securepass for a token, return default values from config file
+ *   if 0: when no value is returned by securepass for a token, return an empty value
  */
 int sp_xattrs (sp_xattrs_t **xattrs, char *sp_username, int get_defaults) {
 	int len;
@@ -530,6 +533,140 @@ int sp_xattrs_p (sp_xattrs_t **xattrs, const char *username, int get_defaults) {
 
 /*
  * returns 1 if SUCCESS, -1 if error
+ * sp_username: specifies the username in SecurePass format, i.e. user at realm
+ * pwd: password to set
+ */
+int sp_user_password_change (const char *sp_username, const char* pwd) {
+
+	int len;
+	jsmntok_t *tok;
+	struct MemoryStruct chunk;
+
+	/*	debug (4, "==> sp_user_password_change sp_username=%s pwd=%s", sp_username, pwd); */
+	debug (4, "==> sp_user_password_change sp_username=%s", sp_username);
+	if ((sp_config.status != SP_INITED)) {
+		if (!(sp_init ())) return -1;
+	}
+	if (sp_username == NULL)  {
+		error ("sp_user_password_change() called with username=NULL");
+		return -1;
+	}
+	if (pwd == NULL)  {
+		error ("sp_user_password_change() called with password=NULL");
+		return -1;
+	}
+	/* call curl */
+	char post_data[(strlen ("USERNAME=") + strlen (sp_username) + strlen ("PASSWORD") + strlen (pwd) + 2)];
+	sprintf (post_data, "USERNAME=%s&PASSWORD=%s", sp_username, pwd);
+	len = do_curl(sp_config.URL_u_pwd_chg, post_data, &tok, (struct MemoryStruct *) &chunk);
+	if (len == -1) {
+		return -1;
+	}
+	/* check for value of rc token */
+	if (!(rc_ok (chunk.memory, tok, len))) {
+		free (tok);
+		free (chunk.memory);
+		return -1;
+	}
+	return 1;
+}
+
+/*
+ * returns 1 if SUCCESS, -1 if error
+ * username: specifies the username in Posix format, i.e. user
+ * pwd: password to set
+ */
+int sp_user_password_change_p (const char *username, const char* pwd) {
+
+	if ((sp_config.status != SP_INITED)) {
+		if (!(sp_init ())) return -1;
+	}
+	if (username == NULL)  {
+		error ("sp_user_password_change_p() called with username=NULL");
+		return -1;
+	}
+	/* concatenate realm to name */
+ 	char sp_name[(strlen (username) + strlen (sp_config.realm) + 2)]; 
+	sprintf (sp_name, "%s%s%s", username, "@", sp_config.realm);
+	return sp_user_password_change (sp_name, pwd);
+}
+
+/*
+ * returns 1 if SUCCESS, -1 if error
+ * sp_username: specifies the username in SecurePass format, i.e. user at realm
+ * secret: concatenation of OTP and password
+ */
+int sp_user_auth (const char *sp_username, const char* secret) {
+
+	int len;
+	jsmntok_t *tok;
+	struct MemoryStruct chunk;
+
+	/* debug (4, "==> sp_user_auth, sp_username=%s secret=%s", sp_username, secret); */
+	debug (4, "==> sp_user_auth, sp_username=%s", sp_username);
+	if ((sp_config.status != SP_INITED)) {
+		if (!(sp_init ())) return -1;
+	}
+	if (sp_username == NULL)  {
+		error ("sp_user_auth() called with username=NULL");
+		return -1;
+	}
+	if (secret == NULL)  {
+		error ("sp_user_auth() called with secret=NULL");
+		return -1;
+	}
+	/* call curl */
+	char post_data[(strlen ("USERNAME=") + strlen (sp_username) + strlen ("SECRET") + strlen (secret) + 2)];
+	sprintf (post_data, "USERNAME=%s&SECRET=%s", sp_username, secret);
+	len = do_curl(sp_config.URL_u_auth, post_data, &tok, (struct MemoryStruct *) &chunk);
+	if (len == -1) {
+		return -1;
+	}
+	/* check for value of rc token */
+	if (!(rc_ok (chunk.memory, tok, len))) {
+		free (tok);
+		free (chunk.memory);
+		return -1;
+	}
+	int r = get_tok (chunk.memory, tok, len, "authenticated");
+	if (r == -1) {
+		debug (1, "token 'authenticated' not found in JSON response");
+	} else {
+		if (TOK_CMP (chunk.memory, tok[r], "true") == 0) {
+			debug (1, "token 'authenticated' has wrong value, expected true");
+			/* set error */
+			r = -1;
+		}
+	}
+	if (r == -1) {
+		free (tok);
+		free (chunk.memory);
+		return -1;
+	}
+	return 1;
+}
+
+/*
+ * returns 1 if SUCCESS, -1 if error
+ * sp_username: specifies the username in Posix format, i.e. user
+ * secret: made up of concatenation of OTP and password
+ */
+int sp_user_auth_p (const char *username, const char* secret) {
+	if ((sp_config.status != SP_INITED)) {
+		if (!(sp_init ())) return -1;
+	}
+	if (username == NULL)  {
+		error ("sp_user_auth_p() called with username=NULL");
+		return -1;
+	}
+	/* concatenate realm to name */
+ 	char sp_name[(strlen (username) + strlen (sp_config.realm) + 2)]; 
+	sprintf (sp_name, "%s%s%s", username, "@", sp_config.realm);
+	return sp_user_auth (sp_name, secret);
+}
+
+/*
+ * returns 1 if SUCCESS, -1 if error
  * uinfo: pointer to a sp_user_info_t - will be allocated by this function
  * 										caller will free() the structure after use
  * username: specifies the username
diff --git a/sp_api.h b/sp_api.h
index caa04ff..3b02f97 100644
--- a/sp_api.h
+++ b/sp_api.h
@@ -18,11 +18,12 @@ struct sp_config {
 	char *URL_u_list;
 	char *URL_u_info;
 	char *URL_u_x_list;
+	char *URL_u_pwd_chg;
+	char *URL_u_auth;
 	char *default_gid;
 	char *default_home;
 	char *default_shell;
 	char *realm;
-
 };
 
 typedef struct {
@@ -53,3 +54,7 @@ int sp_list_users (char ***user, const char *realm);
 int sp_user_info (sp_user_info_t **uinfo, const char *username);
 int sp_xattrs (sp_xattrs_t **xattrs, char *username, int get_defaults);
 int sp_xattrs_p (sp_xattrs_t **xattrs, const char *username, int get_defaults);
+int sp_user_password_change (const char *sp_username, const char* pwd);
+int sp_user_password_change_p (const char *sp_username, const char* pwd);
+int sp_user_auth (const char *sp_username, const char* secret);
+int sp_user_auth_p (const char *sp_username, const char* secret);
diff --git a/sp_client.c b/sp_client.c
index 2bbee97..89f6b76 100644
--- a/sp_client.c
+++ b/sp_client.c
@@ -7,6 +7,8 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <getopt.h>
+#include <ctype.h>
+#include <string.h>
 #include "sp_api.h"
 
 char *realm = NULL;
@@ -14,11 +16,13 @@ char *user = NULL;
 char *xattrs_user = NULL;
 char *app = NULL;
 int list_users = 0;
+char *user_pwd = NULL;
+char *user_secret = NULL;
 
 void get_options (int argc, char *argv[]) 
 {
 	int opt;
-	while ((opt = getopt(argc, argv, "hr:u:x:a:l")) != -1) {
+	while ((opt = getopt(argc, argv, "hr:u:x:a:lw:t:")) != -1) {
                switch (opt) {
                case 'r':
                    realm = optarg;
@@ -35,14 +39,22 @@ void get_options (int argc, char *argv[])
                case 'l':
                    list_users = 1;
                    break;
+               case 'w':
+				   user_pwd = optarg;
+                   break;
+               case 't':
+				   user_secret = optarg;
+                   break;
 				case 'h':
                default: /* '?' */
-                   fprintf(stderr, "Usage: %s [-r realm] [-u user] [-x user] [-a application] [-l] [-h]\n", argv[0]);
+                   fprintf(stderr, "Usage: %s [-r realm] [-u user] [-x user] [-a application] [-l] [-w 'user password'] [-t 'user secret'] [-h]\n", argv[0]);
                    fprintf(stderr, "       -r: apply to specified realm\n");
                    fprintf(stderr, "       -u: get user info\n");
                    fprintf(stderr, "       -x: get user xattrs\n");
                    fprintf(stderr, "       -a: get application info\n");
                    fprintf(stderr, "       -l: get list of users\n");
+                   fprintf(stderr, "       -w: set user password\n");
+                   fprintf(stderr, "       -t: authenticate user (secret is the concatenation of OTP and pwd)\n");
                    fprintf(stderr, "       -h: display usage\n");
                    exit(0);
                }
@@ -87,6 +99,36 @@ void get_xattrs (char *user) {
 	}
 }
 
+void set_passwd (char *user_pwd) {
+	char *user = strtok (user_pwd, " ");
+	char *pwd = strtok (NULL, " ");
+	if ((user == NULL) || (pwd == NULL)) {
+		printf ("argument must be in the form 'user pwd'\n");
+	} else {
+		/* printf ("user=%s pwd=%s\n", user, pwd); */
+		if (sp_user_password_change (user, pwd) == -1) {
+			printf ("sp_user_password_change() returned error\n");
+		} else {
+			printf ("password has been set\n");
+		}	
+	}	
+}
+
+void user_auth (char *user_secret) {
+	char *user = strtok (user_secret, " ");
+	char *secret = strtok (NULL, " ");
+	if ((user == NULL) || (secret == NULL)) {
+		printf ("argument must be in the form 'user secret'\n");
+	} else {
+		/* printf ("user=%s secret=%s\n", user, secret); */
+		if (sp_user_auth (user, secret) == -1) {
+			printf ("sp_user_auth() returned error for user %s\n", user);
+		} else {
+			printf ("user %s has been authenticated\n", user);
+		}	
+	}	
+}
+
 int main(int argc, char *argv[]) {
 	char **user_list;
 	int len, i;
@@ -114,5 +156,11 @@ int main(int argc, char *argv[]) {
 			free (user_list);
 		}
 	}		
+	if (user_pwd) {
+		set_passwd (user_pwd);
+	} 
+	if (user_secret) {
+		user_auth (user_secret);
+	} 
 	return (0);
 }

-- 
libnss-securepass packaging



More information about the Pkg-securepass-commits mailing list