diff --git a/doc/pam_usb.conf b/doc/pam_usb.conf
index 2a035263..dcb2221a 100644
--- a/doc/pam_usb.conf
+++ b/doc/pam_usb.conf
@@ -22,6 +22,13 @@ See https://github.com/mcdope/pam_usb/wiki/Configuration
6F6B-42FC
+
+ Commodore
+ REU
+ CMDKXXXXXXXXXXXXXXXX
+ 6F6B-00FF
+
+
-->
@@ -38,6 +45,7 @@ See https://github.com/mcdope/pam_usb/wiki/Configuration
removal:
MyDevice
+ MySecondDevice
gnome-screensaver-command -\-lock
diff --git a/src/conf.c b/src/conf.c
index 57481632..f77dfff6 100644
--- a/src/conf.c
+++ b/src/conf.c
@@ -88,21 +88,22 @@ static int pusb_conf_device_get_property(
xmlDoc *doc,
const char *property,
char *store,
- size_t size
+ size_t size,
+ char *deviceId
)
{
char *xpath = NULL;
size_t xpath_len;
int retval;
- xpath_len = strlen(CONF_DEVICE_XPATH) + strlen(opts->device.name) + strlen(property) + 1;
+ xpath_len = strlen(CONF_DEVICE_XPATH) + strlen(deviceId) + strlen(property) + 1;
xpath = xmalloc(xpath_len);
if (xpath == NULL) {
log_error("Memory allocation failed\n");
return 0;
}
memset(xpath, 0x00, xpath_len);
- snprintf(xpath, xpath_len, CONF_DEVICE_XPATH, opts->device.name, property);
+ snprintf(xpath, xpath_len, CONF_DEVICE_XPATH, deviceId, property);
retval = pusb_xpath_get_string(doc, xpath, store, size);
xfree(xpath);
return retval;
@@ -110,18 +111,20 @@ static int pusb_conf_device_get_property(
static int pusb_conf_parse_device(
t_pusb_options *opts,
- xmlDoc *doc
+ xmlDoc *doc,
+ int deviceIndex,
+ char *deviceId
)
{
- pusb_conf_device_get_property(opts, doc, "vendor", opts->device.vendor, sizeof(opts->device.vendor));
- pusb_conf_device_get_property(opts, doc, "model", opts->device.model, sizeof(opts->device.model));
+ pusb_conf_device_get_property(opts, doc, "vendor", opts->device_list[deviceIndex].vendor, sizeof(opts->device_list[deviceIndex].vendor), deviceId);
+ pusb_conf_device_get_property(opts, doc, "model", opts->device_list[deviceIndex].model, sizeof(opts->device_list[deviceIndex].model), deviceId);
- if (!pusb_conf_device_get_property(opts, doc, "serial", opts->device.serial, sizeof(opts->device.serial)))
+ if (!pusb_conf_device_get_property(opts, doc, "serial", opts->device_list[deviceIndex].serial, sizeof(opts->device_list[deviceIndex].serial), deviceId))
{
return 0;
}
- pusb_conf_device_get_property(opts, doc, "volume_uuid", opts->device.volume_uuid, sizeof(opts->device.volume_uuid));
+ pusb_conf_device_get_property(opts, doc, "volume_uuid", opts->device_list[deviceIndex].volume_uuid, sizeof(opts->device_list[deviceIndex].volume_uuid), deviceId);
return 1;
}
@@ -177,26 +180,62 @@ int pusb_conf_parse(
return 0;
}
snprintf(device_xpath, sizeof(device_xpath), CONF_USER_XPATH, user, "device");
- retval = pusb_xpath_get_string(
+
+ char *device_list[10] = {
+ xmalloc(128), xmalloc(128), xmalloc(128), xmalloc(128), xmalloc(128),
+ xmalloc(128), xmalloc(128), xmalloc(128), xmalloc(128), xmalloc(128)
+ };
+ for (int currentDevice = 0; currentDevice < 10; currentDevice++)
+ {
+ memset(device_list[currentDevice], 0x0, 128);
+ }
+ retval = pusb_xpath_get_string_list(
doc,
device_xpath,
- opts->device.name,
+ device_list,
sizeof(opts->device.name)
);
- if (!retval || !pusb_conf_parse_device(opts, doc))
+ if (!retval)
{
- log_error("No authentication device configured for user \"%s\".\n", user);
+ log_error("No authentication device(s) configured for user \"%s\".\n", user);
xmlFreeDoc(doc);
xmlCleanupParser();
+
+ for (int currentDevice = 0; currentDevice < 10; currentDevice++)
+ {
+ xfree(device_list[currentDevice]);
+ }
return 0;
}
+
+ for (int currentDevice = 0; currentDevice < 10; currentDevice++)
+ {
+ if (device_list[currentDevice] == NULL || strlen(device_list[currentDevice]) == 0)
+ {
+ continue;
+ }
+
+ strcpy(opts->device_list[currentDevice].name, device_list[currentDevice]);
+ pusb_conf_parse_device(opts, doc, currentDevice, device_list[currentDevice]);
+ }
+
if (!pusb_conf_parse_options(opts, doc, user, service))
{
xmlFreeDoc(doc);
xmlCleanupParser();
+
+ for (int currentDevice = 0; currentDevice < 10; currentDevice++)
+ {
+ xfree(device_list[currentDevice]);
+ }
return (0);
}
xmlFreeDoc(doc);
xmlCleanupParser();
+
+ for (int currentDevice = 0; currentDevice < 10; currentDevice++)
+ {
+ xfree(device_list[currentDevice]);
+ }
return (1);
}
diff --git a/src/conf.h b/src/conf.h
index ace68164..b5327eeb 100644
--- a/src/conf.h
+++ b/src/conf.h
@@ -57,6 +57,7 @@ typedef struct pusb_options
char system_pad_directory[PATH_MAX];
char device_pad_directory[PATH_MAX];
t_pusb_device device;
+ t_pusb_device device_list[10];
} t_pusb_options;
struct s_opt_list
diff --git a/src/device.c b/src/device.c
index def8548c..094821fd 100644
--- a/src/device.c
+++ b/src/device.c
@@ -31,33 +31,47 @@ static int pusb_device_connected(t_pusb_options *opts, UDisksClient *udisks)
GDBusObjectManager *manager = udisks_client_get_object_manager(udisks);
GList *objects = g_dbus_object_manager_get_objects(manager);
int retval = 0;
- int i;
+ int i;
UDisksObject *object = NULL;
UDisksDrive *drive = NULL;
- log_debug("Searching for \"%s\" in the hardware database...\n", opts->device.name);
-
- for (i = 0; i < g_list_length(objects); ++i)
+ for (int currentDevice = 0; currentDevice < 10; currentDevice++)
{
- object = UDISKS_OBJECT(g_list_nth(objects, i)->data);
- if (udisks_object_peek_drive(object))
+ if (strcmp(opts->device_list[currentDevice].name, "") == 0)
{
- drive = udisks_object_get_drive(object);
- retval = strcmp(udisks_drive_get_serial(drive), opts->device.serial) == 0;
+ continue;
+ }
- if (strcmp(opts->device.vendor, "Generic") != 0)
- {
- retval = retval && strcmp(udisks_drive_get_vendor(drive), opts->device.vendor) == 0;
- }
+ log_error("Searching for \"%s\" in the hardware database...\n", opts->device_list[currentDevice].name);
- if (strcmp(opts->device.model, "Generic") != 0)
+ for (i = 0; i < g_list_length(objects); ++i)
+ {
+ object = UDISKS_OBJECT(g_list_nth(objects, i)->data);
+ if (udisks_object_peek_drive(object))
{
- retval = retval && strcmp(udisks_drive_get_model(drive), opts->device.model) == 0;
- }
-
- g_object_unref(drive);
- if (retval) {
- break;
+ drive = udisks_object_get_drive(object);
+ retval = strcmp(udisks_drive_get_serial(drive), opts->device_list[currentDevice].serial) == 0;
+
+ if (strcmp(opts->device_list[currentDevice].vendor, "Generic") != 0)
+ {
+ retval = retval && strcmp(udisks_drive_get_vendor(drive), opts->device_list[currentDevice].vendor) == 0;
+ }
+
+ if (strcmp(opts->device_list[currentDevice].model, "Generic") != 0)
+ {
+ retval = retval && strcmp(udisks_drive_get_model(drive), opts->device_list[currentDevice].model) == 0;
+ }
+
+ g_object_unref(drive);
+ if (retval) {
+ strncpy(opts->device.name, opts->device_list[currentDevice].name, sizeof(opts->device.name) - 1);
+ strncpy(opts->device.vendor, opts->device_list[currentDevice].vendor, sizeof(opts->device.vendor) - 1);
+ strncpy(opts->device.model, opts->device_list[currentDevice].model, sizeof(opts->device.model) - 1);
+ strncpy(opts->device.serial, opts->device_list[currentDevice].serial, sizeof(opts->device.serial) - 1);
+ strncpy(opts->device.volume_uuid, opts->device_list[currentDevice].volume_uuid, sizeof(opts->device.volume_uuid) - 1);
+ currentDevice = 10;
+ break;
+ }
}
}
}
@@ -68,7 +82,7 @@ static int pusb_device_connected(t_pusb_options *opts, UDisksClient *udisks)
}
else
{
- log_error("Authentication device \"%s\" is not connected.\n", opts->device.name);
+ log_error("None of the configured authentication devices is connected.\n");
}
for (i = 0; i < g_list_length(objects); ++i)
@@ -90,7 +104,7 @@ int pusb_device_check(t_pusb_options *opts, const char *user)
if (udisks_client_error != NULL)
{
log_error("Unable to check for device, could not get UDisksClient! Error was: %s\n", udisks_client_error->message);
- g_error_free (udisks_client_error);
+ g_error_free(udisks_client_error);
return (0);
}
diff --git a/src/xpath.c b/src/xpath.c
index 5402ca37..611d180f 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -31,21 +31,21 @@ static xmlXPathObject *pusb_xpath_match(xmlDocPtr doc, const char *path)
if (context == NULL)
{
log_error("Unable to create XML context\n");
- return (NULL);
+ return NULL;
}
result = xmlXPathEvalExpression((xmlChar *)path, context);
xmlXPathFreeContext(context);
if (result == NULL)
{
log_error("Error in xmlXPathEvalExpression\n");
- return (NULL);
+ return NULL;
}
if (xmlXPathNodeSetIsEmpty(result->nodesetval))
{
xmlXPathFreeObject(result);
- return (NULL);
+ return NULL;
}
- return (result);
+ return result;
}
static int pusb_xpath_strip_string(char *dest, const char *src, size_t size)
@@ -71,18 +71,18 @@ static int pusb_xpath_strip_string(char *dest, const char *src, size_t size)
if (first_char == -1 || last_char == -1)
{
- return (0);
+ return 0;
}
if ((last_char - first_char) > (size - 1))
{
log_error("Device name is too long: %s", src);
- return (0);
+ return 0;
}
memset(dest, 0x0, size);
strncpy(dest, &(src[first_char]), last_char - first_char + 1);
- return (1);
+ return 1;
}
int pusb_xpath_get_string(
@@ -98,14 +98,14 @@ int pusb_xpath_get_string(
if (!(result = pusb_xpath_match(doc, path)))
{
- return (0);
+ return 0;
}
if (result->nodesetval->nodeNr > 1)
{
xmlXPathFreeObject(result);
log_debug("Syntax error: %s: more than one record found\n", path);
- return (0);
+ return 0;
}
node = result->nodesetval->nodeTab[0]->xmlChildrenNode;
@@ -114,18 +114,55 @@ int pusb_xpath_get_string(
{
xmlXPathFreeObject(result);
log_debug("Empty value for %s\n", path);
- return (0);
+ return 0;
}
if (!pusb_xpath_strip_string(value, (const char *)result_string, size))
{
xmlFree(result_string);
xmlXPathFreeObject(result);
log_debug("Result for %s (%s) is too long (max: %d)\n", path, (const char *)result_string, size);
- return (0);
+ return 0;
}
xmlFree(result_string);
xmlXPathFreeObject(result);
- return (1);
+ return 1;
+}
+
+int pusb_xpath_get_string_list(
+ xmlDocPtr doc,
+ const char *path,
+ char *values[],
+ size_t size
+)
+{
+ xmlXPathObject *result = NULL;
+ xmlNode *node = NULL;
+ xmlChar *result_string = NULL;
+
+ if (!(result = pusb_xpath_match(doc, path)))
+ {
+ return 0;
+ }
+
+ for (int currentResult = 0; currentResult < result->nodesetval->nodeNr; currentResult++)
+ {
+ node = result->nodesetval->nodeTab[currentResult]->xmlChildrenNode;
+ result_string = xmlNodeListGetString(doc, node, 1);
+ if (!result_string || strcmp("", (char *)result_string) == 0)
+ {
+ log_debug("Empty value for %s\n", path);
+ continue;
+ }
+ if (!pusb_xpath_strip_string(values[currentResult], (char *)result_string, size))
+ {
+ log_debug("Result for %s (%s) is too long (max: %d)\n", path, (const char *)result_string, size);
+ continue;
+ }
+ }
+
+ xmlFree(result_string);
+ xmlXPathFreeObject(result);
+ return 1;
}
int pusb_xpath_get_string_from(
@@ -142,6 +179,11 @@ int pusb_xpath_get_string_from(
xpath_size = strlen(base) + strlen(path) + 1;
xpath = xmalloc(xpath_size);
+ if (xpath == NULL)
+ {
+ log_error("Memory allocation failed\n");
+ return 0;
+ }
memset(xpath, 0x00, xpath_size);
snprintf(xpath, xpath_size, "%s%s", base, path);
retval = pusb_xpath_get_string(doc, xpath, value, size);
@@ -151,7 +193,7 @@ int pusb_xpath_get_string_from(
}
xfree(xpath);
- return (retval);
+ return retval;
}
int pusb_xpath_get_bool(xmlDocPtr doc, const char *path, int *value)
@@ -160,23 +202,23 @@ int pusb_xpath_get_bool(xmlDocPtr doc, const char *path, int *value)
if (!pusb_xpath_get_string(doc, path, ret, sizeof(ret)))
{
- return (0);
+ return 0;
}
if (!strcmp(ret, "true"))
{
*value = 1;
- return (1);
+ return 1;
}
if (!strcmp(ret, "false"))
{
*value = 0;
- return (1);
+ return 1;
}
log_debug("Expecting a boolean, got %s\n", ret);
- return (0);
+ return 0;
}
int pusb_xpath_get_bool_from(
@@ -192,11 +234,16 @@ int pusb_xpath_get_bool_from(
xpath_size = strlen(base) + strlen(path) + 1;
xpath = xmalloc(xpath_size);
+ if (xpath == NULL)
+ {
+ log_error("Memory allocation failed\n");
+ return 0;
+ }
memset(xpath, 0x00, xpath_size);
snprintf(xpath, xpath_size, "%s%s", base, path);
retval = pusb_xpath_get_bool(doc, xpath, value);
xfree(xpath);
- return (retval);
+ return retval;
}
int pusb_xpath_get_time(xmlDocPtr doc, const char *path, time_t *value)
@@ -207,7 +254,7 @@ int pusb_xpath_get_time(xmlDocPtr doc, const char *path, time_t *value)
if (!pusb_xpath_get_string(doc, path, ret, sizeof(ret)))
{
- return (0);
+ return 0;
}
last = &(ret[strlen(ret) - 1]);
@@ -231,16 +278,16 @@ int pusb_xpath_get_time(xmlDocPtr doc, const char *path, time_t *value)
else if (!isdigit(*last))
{
log_debug("Expecting a time modifier, got %c\n", *last);
- return (0);
+ return 0;
}
if (!isdigit(*last))
{
*last = '\0';
}
- *value = (time_t) atoi(ret) * coef;
+ *value = (time_t)atoi(ret) * coef;
- return (0);
+ return 1;
}
int pusb_xpath_get_time_from(
@@ -256,11 +303,16 @@ int pusb_xpath_get_time_from(
xpath_size = strlen(base) + strlen(path) + 1;
xpath = xmalloc(xpath_size);
+ if (xpath == NULL)
+ {
+ log_error("Memory allocation failed\n");
+ return 0;
+ }
memset(xpath, 0x00, xpath_size);
snprintf(xpath, xpath_size, "%s%s", base, path);
retval = pusb_xpath_get_time(doc, xpath, value);
xfree(xpath);
- return (retval);
+ return retval;
}
int pusb_xpath_get_int(xmlDocPtr doc, const char *path, int *value)
@@ -269,11 +321,11 @@ int pusb_xpath_get_int(xmlDocPtr doc, const char *path, int *value)
if (!pusb_xpath_get_string(doc, path, ret, sizeof(ret)))
{
- return (0);
+ return 0;
}
*value = atoi(ret);
- return (1);
+ return 1;
}
int pusb_xpath_get_int_from(
@@ -289,9 +341,14 @@ int pusb_xpath_get_int_from(
xpath_size = strlen(base) + strlen(path) + 1;
xpath = xmalloc(xpath_size);
+ if (xpath == NULL)
+ {
+ log_error("Memory allocation failed\n");
+ return 0;
+ }
memset(xpath, 0x00, xpath_size);
snprintf(xpath, xpath_size, "%s%s", base, path);
retval = pusb_xpath_get_int(doc, xpath, value);
xfree(xpath);
- return (retval);
+ return retval;
}
diff --git a/src/xpath.h b/src/xpath.h
index 6b6bcc50..9343061b 100644
--- a/src/xpath.h
+++ b/src/xpath.h
@@ -20,6 +20,7 @@
# include
int pusb_xpath_get_string(xmlDocPtr doc, const char *path, char *value, size_t size);
+int pusb_xpath_get_string_list(xmlDocPtr doc, const char *path, char *value[], size_t size);
int pusb_xpath_get_string_from(xmlDocPtr doc, const char *base, const char *path, char *value, size_t size);
int pusb_xpath_get_bool(xmlDocPtr doc, const char *path, int *value);
int pusb_xpath_get_bool_from(xmlDocPtr doc, const char *base, const char *path, int *value);
diff --git a/tests/can-actually-be-used/create-image.sh b/tests/can-actually-be-used/create-image.sh
index a2654eb0..433c1dc7 100755
--- a/tests/can-actually-be-used/create-image.sh
+++ b/tests/can-actually-be-used/create-image.sh
@@ -3,3 +3,4 @@
set -e
fallocate -l 16M virtual_usb.img
+fallocate -l 16M virtual_usb_alt.img
diff --git a/tests/can-actually-be-used/mount-image.sh b/tests/can-actually-be-used/mount-image.sh
index b85de3d8..6469a1fd 100755
--- a/tests/can-actually-be-used/mount-image.sh
+++ b/tests/can-actually-be-used/mount-image.sh
@@ -3,7 +3,7 @@
set -e
# Load module with image
-sudo modprobe g_mass_storage file=./virtual_usb.img stall=0 removable=y iSerialNumber=1234567890
+sudo modprobe g_mass_storage file=./virtual_usb.img stall=0 removable=y iSerialNumber=1234567890 iProduct=FirstStick
echo "Info: sleeping 5s to ensure kernel picks up our new device..."
sleep 5
@@ -18,4 +18,3 @@ sudo mkfs.vfat "/dev/"$CREATED_DEVICE"1"
# Create mountpoint and mount fake stick
mkdir -p /tmp/fakestick
sudo mount -t vfat "/dev/"$CREATED_DEVICE"1" /tmp/fakestick -o rw,umask=0000
-
diff --git a/tests/can-actually-be-used/run-tests.sh b/tests/can-actually-be-used/run-tests.sh
index 05d6603f..b10d6486 100755
--- a/tests/can-actually-be-used/run-tests.sh
+++ b/tests/can-actually-be-used/run-tests.sh
@@ -9,6 +9,6 @@ rm -rf /home/`whoami`/.pamusb
./test-conf-detects-device.sh && \
./test-conf-adds-device.sh && \
./test-conf-adds-user.sh && \
-./test-conf-doesnt-add-user-twice.sh && \
+./test-conf-doesnt-add-user-twice-but-adds-a-second-device.sh && \
./test-check-verify-created-config.sh && \
./test-agent-properly-triggers.sh
diff --git a/tests/can-actually-be-used/test-agent-properly-triggers.sh b/tests/can-actually-be-used/test-agent-properly-triggers.sh
index 1d8f7ea2..4ed675cc 100755
--- a/tests/can-actually-be-used/test-agent-properly-triggers.sh
+++ b/tests/can-actually-be-used/test-agent-properly-triggers.sh
@@ -13,12 +13,12 @@ sleep 5 # make sure agent is up
sync && sync && sync
sudo umount /tmp/fakestick
sudo modprobe -r g_mass_storage || exit 1
-sleep 10
+sleep 20
sudo tail -n 200 /var/log/auth.log | grep "pamusb-agent\[" | grep "has been removed, locking down user" > /dev/null && echo -e "\t\t\t\tLock event found" || { echo -e "\t\t\t\tNo lock event found!"; exit 1; }
# "plug" virtual usb
sudo modprobe g_mass_storage file=./virtual_usb.img stall=0 removable=y iSerialNumber=1234567890 || exit 1
-sleep 10
+sleep 20
sudo tail -n 200 /var/log/auth.log | grep "pamusb-agent\[" | grep "Authentication succeeded. Unlocking user" > /dev/null && echo -e "\t\t\t\tUnlock event found" || { echo -e "\t\t\t\tNo unlock event found!"; exit 1; }
# Disable agent again
diff --git a/tests/can-actually-be-used/test-check-verify-created-config.sh b/tests/can-actually-be-used/test-check-verify-created-config.sh
index 2217f00a..dd0b0393 100755
--- a/tests/can-actually-be-used/test-check-verify-created-config.sh
+++ b/tests/can-actually-be-used/test-check-verify-created-config.sh
@@ -1,4 +1,4 @@
#!/usr/bin/bash
echo -e "Test:\t\t\tpamusb-check verifies the config previous tests created / reports granted"
-pamusb-check `whoami` 2>&1 && echo -e "Result:\t\t\tPASSED!" || exit 1
\ No newline at end of file
+pamusb-check --debug `whoami` && echo -e "Result:\t\t\tPASSED!" || exit 1
\ No newline at end of file
diff --git a/tests/can-actually-be-used/test-conf-adds-device.sh b/tests/can-actually-be-used/test-conf-adds-device.sh
index f3cfbe5e..28dcec22 100755
--- a/tests/can-actually-be-used/test-conf-adds-device.sh
+++ b/tests/can-actually-be-used/test-conf-adds-device.sh
@@ -1,4 +1,4 @@
#!/usr/bin/bash
echo -e "Test:\t\t\tpamusb-conf properly add device(s)"
echo -en "pamusb-conf output:\t" # to fake the unhideable python output as expected output :P
-sudo pamusb-conf --add-device=test --device=0 --volume=0 --yes | grep "Done" && cat /etc/security/pam_usb.conf | grep "File-Stor Gadget" > /dev/null && echo -e "Result:\t\t\tPASSED!" || exit 1
\ No newline at end of file
+sudo pamusb-conf --add-device=test --device=0 --volume=0 --yes | grep "Done" && cat /etc/security/pam_usb.conf | grep "1234567890" > /dev/null && echo -e "Result:\t\t\tPASSED!" || exit 1
\ No newline at end of file
diff --git a/tests/can-actually-be-used/test-conf-doesnt-add-user-twice-but-adds-a-second-device.sh b/tests/can-actually-be-used/test-conf-doesnt-add-user-twice-but-adds-a-second-device.sh
new file mode 100755
index 00000000..484e8014
--- /dev/null
+++ b/tests/can-actually-be-used/test-conf-doesnt-add-user-twice-but-adds-a-second-device.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/bash
+
+echo -e "Test:\t\t\tpamusb-conf doesn't add user(s) twice, but it adds a second device for an existing user"
+
+# "unplug" virtual usb
+sync && sync && sync
+sudo umount /tmp/fakestick
+sudo modprobe -r g_mass_storage || exit 1
+sleep 10
+
+# "plug" another virtual usb
+sudo modprobe g_mass_storage file=./virtual_usb.img stall=0 removable=y iSerialNumber=1234567891 iProduct=SecondStick || exit 1
+sleep 10
+CREATED_DEVICE=$(lsblk | grep 16M | awk '{ print $1 }')
+sudo mount -t vfat "/dev/"$CREATED_DEVICE"1" /tmp/fakestick -o rw,umask=0000
+
+echo -en "pamusb-conf --add-device output:\t" # to fake the unhideable python output as expected output :P
+sudo pamusb-conf --add-device=test2 --device=0 --volume=0 --yes | grep "Done" && cat /etc/security/pam_usb.conf | grep "1234567891" > /dev/null && echo -e "Result:\t\t\tPASSED!" || exit 1
+
+echo -en "pamusb-conf --add-user output:\t" # to fake the unhideable python output as expected output :P
+sudo pamusb-conf --add-user=`whoami` --device=1 --yes | grep "Done" && cat /etc/security/pam_usb.conf | grep "test2" > /dev/null && echo -e "Result:\t\t\tPASSED!" || exit 1
diff --git a/tests/can-actually-be-used/test-conf-doesnt-add-user-twice.sh b/tests/can-actually-be-used/test-conf-doesnt-add-user-twice.sh
deleted file mode 100755
index 3760509b..00000000
--- a/tests/can-actually-be-used/test-conf-doesnt-add-user-twice.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/bash
-
-# @todo: this will fail if other devices are present. its missing a step to actually determine the number used internally
-# @todo: check if the numbering used in debconf is correct after all
-
-echo -e "Test:\t\t\tpamusb-conf doesn't add user(s) twice"
-sudo pamusb-conf --add-user=`whoami` --device=0 --yes | grep "already added" > /dev/null && echo -e "Result:\t\t\tPASSED!" || exit 1
\ No newline at end of file
diff --git a/tools/pamusb-agent b/tools/pamusb-agent
index bf4575e4..428364a7 100755
--- a/tools/pamusb-agent
+++ b/tools/pamusb-agent
@@ -40,9 +40,10 @@ from gi.repository import GLib, UDisks
import xml.etree.ElementTree as et
class HotPlugDevice:
- def __init__(self, serial):
+ def __init__(self, serial, name):
self.__udi = None
self.__serial = serial
+ self.__name = name
self.__callbacks = []
self.__running = False
@@ -62,6 +63,9 @@ class HotPlugDevice:
return False
+ def getWatchedDeviceName(self):
+ return self.__name
+
def __scanDevices(self):
for udi in udisksObjectManager.get_objects():
if udi.get_block():
@@ -92,7 +96,7 @@ class HotPlugDevice:
return
self.__udi = udi
if self.__running:
- [ cb('added') for cb in self.__callbacks ]
+ [ cb('added', self.__name) for cb in self.__callbacks ]
def __deviceRemoved(self, udi):
if self.__udi is None:
@@ -101,7 +105,7 @@ class HotPlugDevice:
return
self.__udi = None
if self.__running:
- [ cb('removed') for cb in self.__callbacks ]
+ [ cb('removed', self.__name) for cb in self.__callbacks ]
class Log:
def __init__(self):
@@ -209,23 +213,34 @@ def userDeviceThread(user):
}
)
- deviceName = user.find('device').text.strip()
+ devices_for_user = []
+ to_watch = []
+
+ all_devices = doc.findall("devices/device")
+ user_devices = user.findall("device")
+ for device in user_devices:
+ devices_for_user.append(device.text)
- devices = doc.findall("devices/device")
deviceOK = False
- for device in devices:
- if device.get('id') == deviceName:
+ for device in all_devices:
+ if device.get('id') in devices_for_user:
+ to_watch.append({"name": device.get('id'), "serial": device.findtext('serial')})
deviceOK = True
- break
- if not deviceOK:
- logger.error('Device %s not found in configuration file.' % deviceName)
+ if not deviceOK or len(to_watch) == 0:
+ logger.error('Device(s) not found in configuration file.')
return 1
- serial = device.find('serial').text.strip()
resumeTimestamp = datetime.datetime.min
- def authChangeCallback(event):
+ def authChangeCallback(event, deviceName):
+ nonlocal hpDevs
+
+ for otherDeviceThread in hpDevs:
+ if not otherDeviceThread.getWatchedDeviceName() == deviceName and otherDeviceThread.isDeviceConnected():
+ logger.info('Device "%s" removed or plugged but another one is connected anyway, ignoring' % deviceName)
+ return
+
if event == 'removed':
nonlocal resumeTimestamp
currentTimestamp = datetime.datetime.now()
@@ -267,7 +282,7 @@ def userDeviceThread(user):
logger.info('Process exit code: %d' % (process.returncode))
logger.info('Process stdout: %s' % (process.stdout.decode()))
logger.info('Process stderr: %s' % (process.stderr.decode()))
-
+
else:
logger.info('No commands defined for unlock!')
@@ -279,28 +294,37 @@ def userDeviceThread(user):
def onSuspendOrResume(start, member=None):
nonlocal resumeTimestamp
- nonlocal hpDev
+ nonlocal hpDevs
- if start == True:
- logger.info('Suspending user "%s"' % (userName))
- resumeTimestamp = datetime.datetime.max
- else:
- logger.info('Resuming user "%s"' % (userName))
- if hpDev.isDeviceConnected() == True:
- logger.info('Device is connected for user "%s", unlocking' % (userName))
- authChangeCallback('added')
+ for hpDev in hpDevs:
+ if start == True:
+ logger.info('Suspending user "%s"' % (userName))
+ resumeTimestamp = datetime.datetime.max
+ else:
+ logger.info('Resuming user "%s"' % (userName))
+ if hpDev.isDeviceConnected() == True:
+ logger.info('Device %s is connected for user "%s", unlocking' % (hpDev.__name, userName))
+ authChangeCallback('added')
- resumeTimestamp = datetime.datetime.now()
+ resumeTimestamp = datetime.datetime.now()
login1Interface = login1ManagerDBusIface()
for signal in ['PrepareForSleep', 'PrepareForShutdown']:
login1Interface.connect_to_signal(signal, onSuspendOrResume, member_keyword='member')
- hpDev = HotPlugDevice(serial)
- hpDev.addCallback(authChangeCallback)
+ hpDevs = []
+ threads = []
+ for watch_this in to_watch:
+ logger.info('Watching device "%s" for user "%s"' % (watch_this.get('name'), userName))
+
+ hpDev = HotPlugDevice(watch_this.get('serial'), watch_this.get('name'))
+ hpDev.addCallback(authChangeCallback)
- logger.info('Watching device "%s" for user "%s"' % (deviceName, userName))
- hpDev.run()
+ thread = threading.Thread(target=hpDev.run)
+ thread.start()
+
+ threads.append(thread)
+ hpDevs.append(hpDev)
udisks = UDisks.Client.new_sync()
udisksObjectManager = udisks.get_object_manager()
@@ -355,10 +379,10 @@ if options['daemon'] and os.fork():
sys.exit(0)
def sig_handler(sig, frame):
- logger.info('Stopping agent.')
- sys.exit(0)
+ logger.info('Stopping agent.')
+ sys.exit(0)
sys_signals = ['SIGINT', 'SIGTERM', 'SIGTSTP', 'SIGTTIN', 'SIGTTOU']
for i in sys_signals:
- signal.signal(getattr(signal, i), sig_handler)
+ signal.signal(getattr(signal, i), sig_handler)
diff --git a/tools/pamusb-conf b/tools/pamusb-conf
index eae94632..f22d9283 100755
--- a/tools/pamusb-conf
+++ b/tools/pamusb-conf
@@ -127,13 +127,6 @@ def addUser(options):
print('You must add a device (--add-device) before adding users')
sys.exit(1)
- alreadyConfiguredUsers = doc.getElementsByTagName('user')
- if len(alreadyConfiguredUsers) > 0:
- for user in alreadyConfiguredUsers:
- if user.getAttribute('id') == options['userName']:
- print('This user is already added to the configuration, to edit it you need to do it manually currently - sorry.')
- sys.exit(1)
-
devices = []
for device in devicesObj:
devices.append(device.getAttribute('id'))
@@ -151,14 +144,29 @@ def addUser(options):
]
)
+ # Check if user exists
users = doc.getElementsByTagName('users')
- user = doc.createElement('user')
- user.attributes['id'] = options['userName']
- e = doc.createElement('device')
- t = doc.createTextNode(device)
- e.appendChild(t)
- user.appendChild(e)
- users[0].appendChild(prettifyElement(user))
+ userElements = doc.getElementsByTagName('user')
+ user = False
+ for _user in userElements:
+ if _user.getAttribute('id') == options['userName']:
+ user = _user
+ break
+
+ if user is False: # does not exist, lets create
+ user = doc.createElement('user')
+ user.attributes['id'] = options['userName']
+ e = doc.createElement('device')
+ t = doc.createTextNode(device)
+ e.appendChild(t)
+ user.appendChild(e)
+ users[0].appendChild(prettifyElement(user))
+ else: # just add another device
+ e = doc.createElement('device')
+ t = doc.createTextNode(device)
+ e.appendChild(t)
+ user.appendChild(e)
+
writeConf(options, doc)
def listAvailableDevicesAndVolumes(options):