Key Manager tizen.org session and user management 63/37263/11
authorMaciej J. Karpiuk <m.karpiuk2@samsung.com>
Mon, 23 Mar 2015 15:13:07 +0000 (16:13 +0100)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Mon, 13 Apr 2015 14:26:31 +0000 (07:26 -0700)
integration.

Key-Manager integrates with PAM (via pam_key_manager_plugin.so lib
and appropriate configuration changes) and gumd via user removal hook.

PAM configuration needs to be changed to use the .so specified above.
For testing, do the following changes in /etc/pam.d/system-auth:

section password:
* remove pam_deny.so line
* change pam_unix.so from sufficient to required
* add "password    optional      pam_key_manager_plugin.so change_step=before" before the pam_unix.so entry
* add "password    optional      pam_key_manager_plugin.so change_step=after" after the pam_unix.so entry

section session:
* add "session     optional      pam_key_manager_plugin.so" as last item

Change-Id: I2fd29ab527aa3d89c810b9c6d5f74cbbec2e5957

CMakeLists.txt
data/gumd/10_key-manager.post [new file with mode: 0755]
packaging/key-manager.spec
src/CMakeLists.txt
src/include/ckmc/ckmc-manager.h
src/pam_plugin/CMakeLists.txt [new file with mode: 0644]
src/pam_plugin/pam-key-manager-plugin.cpp [new file with mode: 0644]
tools/CMakeLists.txt
tools/ckm_tool.cpp [new file with mode: 0644]

index 40f7ce2..ab1548c 100644 (file)
@@ -74,6 +74,7 @@ SET(TARGET_KEY_MANAGER_CLIENT "key-manager-client")
 SET(TARGET_KEY_MANAGER_CONTROL_CLIENT "key-manager-control-client")
 SET(TARGET_KEY_MANAGER_COMMON "key-manager-common")
 SET(TARGET_LISTENER "key-manager-listener")
+SET(TARGET_PAM_KEY_MANAGER_PLUGIN "pam_key_manager_plugin")
 
 SET(TARGET_TEST_MERGED "ckm-tests-internal")
 
diff --git a/data/gumd/10_key-manager.post b/data/gumd/10_key-manager.post
new file mode 100755 (executable)
index 0000000..85f82c7
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# $1 - user name
+# $2 - uid
+# $3 - gid
+# call key-manager tool for locking the database and removing user entries
+ckm_tool -d $2
index bcb7e40..7cf1357 100644 (file)
@@ -79,6 +79,19 @@ Requires:   key-manager = %{version}-%{release}
 %description -n key-manager-tests
 Internal test for key-manager implementation.
 
+%package -n key-manager-pam-plugin
+Summary:    CKM login/password module to PAM.
+Group:      Development/Libraries
+BuildRequires: pam-devel
+Requires:   key-manager = %{version}-%{release}
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+%description -n key-manager-pam-plugin
+CKM login/password module to PAM.
+It's used to monitor user login/logout and password change events from PAM.
+
+
 %prep
 %setup -q
 cp -a %{SOURCE1001} .
@@ -119,6 +132,8 @@ cp data/scripts/*.sql %{buildroot}/usr/share/ckm/scripts
 mkdir -p %{buildroot}/usr/share/ckm-db-test
 cp tests/testme_ver1.db %{buildroot}/usr/share/ckm-db-test/
 cp tests/testme_ver2.db %{buildroot}/usr/share/ckm-db-test/
+mkdir -p %{buildroot}/etc/gumd/userdel.d/
+cp data/gumd/10_key-manager.post %{buildroot}/etc/gumd/userdel.d/
 
 %make_install
 mkdir -p %{buildroot}%{_unitdir}/multi-user.target.wants
@@ -201,6 +216,9 @@ fi
 %{_datadir}/ckm/scripts/*.sql
 %attr(444, root, root) %{_datadir}/ckm/scripts/*.sql
 /etc/opt/upgrade/230.key-manager-migrate-dkek.patch.sh
+/etc/gumd/userdel.d/10_key-manager.post
+%attr(550, root, root) /etc/gumd/userdel.d/10_key-manager.post
+%{_bindir}/ckm_tool
 
 %files -n key-manager-listener
 %manifest key-manager-listener.manifest
@@ -246,3 +264,7 @@ fi
 %{_datadir}/ckm-db-test/testme_ver1.db
 %{_datadir}/ckm-db-test/testme_ver2.db
 %{_bindir}/ckm_so_loader
+
+%files -n key-manager-pam-plugin
+%defattr(-,root,root,-)
+%{_libdir}/security/pam_key_manager_plugin.so*
index 658b0c4..ed6db61 100644 (file)
@@ -191,3 +191,4 @@ INSTALL(FILES
 
 ADD_SUBDIRECTORY(manager)
 ADD_SUBDIRECTORY(listener)
+ADD_SUBDIRECTORY(pam_plugin)
index 3c2ac43..ed4113d 100644 (file)
@@ -47,8 +47,8 @@ extern "C" {
  * @privlevel public
  * @privilege %http://tizen.org/privilege/keymanager
  *
- * @remarks Currently only six types of keys are supported for this API. These are RSA
- *          public/private key, DSA public/private key and ECDSA public/private key.
+ * @remarks Currently API supports seven types of keys. These are RSA public/private key,
+ *          DSA public/private key, ECDSA public/private key and AES symmetric key.
  * @remarks key_type in key may be set to #CKMC_KEY_NONE as an input. key_type is determined inside
  *          key manager during storing keys.
  * @remarks Some private key files are protected by a password. If raw_key in key read from those
diff --git a/src/pam_plugin/CMakeLists.txt b/src/pam_plugin/CMakeLists.txt
new file mode 100644 (file)
index 0000000..36ccf8c
--- /dev/null
@@ -0,0 +1,23 @@
+SET(PAM_KEY_MANAGER_PLUGIN_SOURCES
+    ${PROJECT_SOURCE_DIR}/src/pam_plugin/pam-key-manager-plugin.cpp
+    )
+
+INCLUDE_DIRECTORIES(SYSTEM
+    ${PAM_KEY_MANAGER_PLUGIN_DEP_INCLUDE_DIRS}
+    )
+
+ADD_LIBRARY(${TARGET_PAM_KEY_MANAGER_PLUGIN} SHARED ${PAM_KEY_MANAGER_PLUGIN_SOURCES})
+
+SET_TARGET_PROPERTIES(
+    ${TARGET_PAM_KEY_MANAGER_PLUGIN}
+    PROPERTIES
+        PREFIX ""
+        COMPILE_FLAGS "-fvisibility=hidden"
+    )
+
+TARGET_LINK_LIBRARIES(${TARGET_PAM_KEY_MANAGER_PLUGIN}
+    ${TARGET_KEY_MANAGER_CONTROL_CLIENT}
+    ${TARGET_KEY_MANAGER_COMMON}
+    )
+
+INSTALL(TARGETS ${TARGET_PAM_KEY_MANAGER_PLUGIN} LIBRARY DESTINATION ${LIB_INSTALL_DIR}/security NAMELINK_SKIP)
diff --git a/src/pam_plugin/pam-key-manager-plugin.cpp b/src/pam_plugin/pam-key-manager-plugin.cpp
new file mode 100644 (file)
index 0000000..0cf0943
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ *  Copyright (c) 2000 - 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+/*
+ * @file        pam-key-manager-plugin.cpp
+ * @author      Maciej Karpiuk (m.karpiuk2@samsung.com)
+ * @version     1.0
+ * @brief       PAM module to handle session and password events.
+ */
+
+#include <sys/param.h>
+
+#include <string>
+#include <pwd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <symbol-visibility.h>
+#include <security/pam_modules.h>
+#include <security/pam_ext.h>
+#include <security/pam_appl.h>
+#include <syslog.h>
+#include <shadow.h>
+#include <ckm/ckm-control.h>
+
+namespace
+{
+#define PASSWORD_SHADOWED   "x"
+std::string old_password;
+
+bool identify_user_pwd(pam_handle_t *pamh, uid_t & uid, std::string & passwd)
+{
+    int pam_err;
+    const char *user;
+    if ((pam_err = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS)
+        return true;
+    struct passwd *pwd;
+    if ((pwd = getpwnam(user)) == NULL)
+        return true;
+    if(strcmp(pwd->pw_passwd, PASSWORD_SHADOWED)==0)
+    {
+        struct spwd *pwd_sh;
+        if ((pwd_sh = getspnam(user)) == NULL)
+            return true;
+        passwd = std::string(pwd_sh->sp_pwdp);
+    }
+    else
+        passwd = std::string(pwd->pw_passwd);
+    uid = pwd->pw_uid;
+    return false;
+}
+}
+
+COMMON_API PAM_EXTERN int
+pam_sm_open_session(pam_handle_t *pamh, int /*flags*/, int /*argc*/, const char **/*argv*/)
+{
+    // identify user
+    uid_t uid = -1;
+    std::string passwd;
+    if(identify_user_pwd(pamh, uid, passwd))
+        return PAM_SESSION_ERR;
+
+    auto control = CKM::Control::create();
+    int ec = control->unlockUserKey(uid, passwd.c_str());
+    if(ec == CKM_API_SUCCESS)
+        return PAM_SUCCESS;
+
+    if(ec == CKM_API_ERROR_AUTHENTICATION_FAILED)
+    {
+        pam_syslog(pamh, LOG_ERR, "key-manager and system password desynchronized,"
+                                  "removing key-manager database for user: %d\n", uid);
+
+        // key-manager<->system password desync
+        // remove the user content
+        ec = control->removeUserData(uid);
+        if(ec == CKM_API_SUCCESS) {
+            ec = CKM::Control::create()->unlockUserKey(uid, passwd.c_str());
+            if(ec == CKM_API_SUCCESS)
+                return PAM_SUCCESS;
+            pam_syslog(pamh, LOG_ERR, "key-manager and system password desynchronized,"
+                                      "attempt to create new database failed: %d\n", ec);
+        } else {
+            pam_syslog(pamh, LOG_ERR, "key-manager and system password desynchronized and"
+                                      "recovery attempt to remove broken database failed: %d\n", ec);
+        }
+    }
+
+    return PAM_SESSION_ERR;
+}
+
+COMMON_API PAM_EXTERN int
+pam_sm_close_session(pam_handle_t *pamh, int /*flags*/, int /*argc*/, const char **/*argv*/)
+{
+    // identify user
+    uid_t uid = -1;
+    std::string passwd;
+    if(identify_user_pwd(pamh, uid, passwd))
+        return PAM_SESSION_ERR;
+
+    if(CKM::Control::create()->lockUserKey(uid) == CKM_API_SUCCESS)
+        return PAM_SUCCESS;
+
+    return PAM_SESSION_ERR;
+}
+
+COMMON_API PAM_EXTERN int
+pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+    if(argc==0) {
+        pam_syslog(pamh, LOG_ERR, "key-manager plugin called with inappropriate arguments\n");
+        return PAM_SERVICE_ERR;
+    }
+
+    // identify user
+    uid_t uid = -1;
+    std::string passwd;
+    if(identify_user_pwd(pamh, uid, passwd))
+        return PAM_USER_UNKNOWN;
+
+    // attention: argv[0] is the argument, not the binary/so name
+    // args are in arg_name=value format
+    if(strstr(argv[0], "change_step"))
+    {
+        if(strstr(argv[0], "before"))
+        {
+            if( ! (flags & PAM_PRELIM_CHECK))
+                old_password = passwd;
+            return PAM_SUCCESS;
+        }
+        else if(strstr(argv[0], "after"))
+        {
+            if(flags & PAM_PRELIM_CHECK)
+                return PAM_SUCCESS;
+
+            if(old_password.size() == 0) {
+                pam_syslog(pamh, LOG_ERR, "attempt to change key-manager password w/o old password\n");
+                return PAM_SERVICE_ERR;
+            }
+            std::string local_old_pwd = old_password;
+            old_password.clear();
+
+            // CKM does not allow to change user password if database does
+            // not exists. We must create database before change password.
+            auto ctrl = CKM::Control::create();
+            int ec = ctrl->unlockUserKey(uid, local_old_pwd.c_str());
+            if (CKM_API_SUCCESS != ec) {
+                // no DB reset here: somebody else might have changed password in mean time
+                // if desync happened, next login attempt will remove the DB
+                pam_syslog(pamh, LOG_ERR, "attempt to change key-manager password failed:"
+                                          "can not open/create the database, ec: %d\n", ec);
+                return PAM_SERVICE_ERR;
+            }
+
+            ec = ctrl->changeUserPassword(uid, local_old_pwd.c_str(), passwd.c_str());
+            if (CKM_API_SUCCESS != ec) {
+                pam_syslog(pamh, LOG_ERR, "attempt to change key-manager password ec: %d\n", ec);
+                return PAM_SERVICE_ERR;
+            }
+
+            return PAM_SUCCESS;
+        }
+    }
+
+    pam_syslog(pamh, LOG_ERR, "key-manager plugin called with no valid \"change_step\" option setting\n");
+    return PAM_SERVICE_ERR;
+}
index cdb71b6..f1df926 100644 (file)
@@ -1,7 +1,7 @@
 SET(CKM_SO_LOADER "ckm_so_loader")
 
 SET(CKM_SO_LOADER_SOURCES ${PROJECT_SOURCE_DIR}/tools/ckm_so_loader.cpp)
-
+SET(KEY_MANAGER_SRC_PATH ${PROJECT_SOURCE_DIR}/src)
 ADD_EXECUTABLE( ${CKM_SO_LOADER} ${CKM_SO_LOADER_SOURCES} )
 
 #linker directories
@@ -18,3 +18,31 @@ INSTALL(TARGETS ${CKM_SO_LOADER}
                 WORLD_READ
                 WORLD_EXECUTE
      )
+
+
+SET(CKM_TOOL "ckm_tool")
+SET(CKM_TOOL_SOURCES ${PROJECT_SOURCE_DIR}/tools/ckm_tool.cpp)
+
+INCLUDE_DIRECTORIES(
+    ${KEY_MANAGER_SRC_PATH}/include
+    )
+
+ADD_EXECUTABLE( ${CKM_TOOL} ${CKM_TOOL_SOURCES} )
+
+#linker directories
+TARGET_LINK_LIBRARIES(${CKM_TOOL}
+    ${TARGET_KEY_MANAGER_CONTROL_CLIENT}
+    )
+
+#place for output file
+INSTALL(TARGETS ${CKM_TOOL}
+    DESTINATION /usr/bin
+    PERMISSIONS OWNER_READ
+                OWNER_WRITE
+                OWNER_EXECUTE
+                GROUP_READ
+                GROUP_EXECUTE
+                WORLD_READ
+                WORLD_EXECUTE
+     )
+
diff --git a/tools/ckm_tool.cpp b/tools/ckm_tool.cpp
new file mode 100644 (file)
index 0000000..0af2980
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ *  Copyright (c) 2000 - 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+/*
+ * @file       ckm_tool.cpp
+ * @author     Maciej J. Karpiuk (m.karpiuk2@samsung.com)
+ * @version    1.0
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <iostream>
+#include <errno.h>
+#include <ckm/ckm-error.h>
+#include <ckm/ckm-control.h>
+
+using namespace std;
+
+bool parseLong(const char *buf_ptr, long int &val)
+{
+    char *temp;
+    errno = 0;
+    long int val_tmp = strtol(buf_ptr, &temp, 0);
+    if(errno)
+        return true;
+    val = val_tmp;
+    return false;
+}
+
+int main(int argc, char* argv[])
+{
+    if (argc < 3) {
+        cerr << "Usage: ckm_tool [option] [opt_arg]" << endl;
+        cerr << "option: " << endl;
+        cerr << "\t-d\tdelete user database, opt_arg specified the user UID" << endl;
+        cerr << "Example: ckm_tool -l 5000" << endl;
+        return -1;
+    }
+
+    // simple input arg parser
+    for (int i=1; i<argc-1; i++)
+    {
+        long int uid;
+        if(!strcmp(argv[i], "-d"))
+        {
+            if(parseLong(argv[i+1], uid) || uid<0) {
+                cerr << "parameter error: invalid UID provided to the -d option" << endl;
+                exit(-2);
+            }
+
+            // lock the database
+            auto control = CKM::Control::create();
+            int ec = control->lockUserKey(static_cast<uid_t>(uid));
+            if(ec != CKM_API_SUCCESS) {
+                cerr << "Failed, lock DB error: " << ec << endl;
+                exit(ec);
+            }
+
+            // remove the user content
+            ec = control->removeUserData(static_cast<uid_t>(uid));
+            if(ec != CKM_API_SUCCESS) {
+                cerr << "Failed, remove user data error: " << ec << endl;
+                exit(ec);
+            }
+        }
+        else {
+            std::cout << "Not enough or invalid arguments, please try again.\n";
+            exit(-1);
+        }
+    }
+
+    return 0;
+}