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):