/* YOUR FILE-HEADER COMMENT HERE */ /* * This file uses kernel-doc style comments, which is similar to * Javadoc and Doxygen-style comments. See * ~/linux/Documentation/doc-guide/kernel-doc.rst for details. */ /* * Getting compilation warnings? The Linux kernel is written against * C89, which means: * - No // comments, and * - All variables must be declared at the top of functions. * Read ~/linux/Documentation/process/coding-style.rst to ensure your * project compiles without warnings. */ #define pr_fmt(fmt) "pwkeeper: " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xt_cs421net.h" #define MASTERPW_LEN 32 #define ACCOUNTNAME_LEN 16 #define ACCOUNTPW_LEN 16 /** * sha3_digest() - calculate the SHA-3 digest for an arbitrary input buffer * @input: input data buffer * @input_len: number of bytes in @input * @digest: destination pointer to store digest * @digest_len: size of digest buffer (in/out parameter) * * Hash the input buffer pointed to by @input, up to @input_len * bytes. Store the resulting digest at @digest. Afterwards, update * the value pointed to by @digest_len by the size of the stored * digest. * * You do not need to modify this function. * * Return: 0 on success, negative on error */ static int sha3_digest(const void *input, size_t input_len, u8 * digest, size_t * digest_len) { struct crypto_shash *sha3_tfm; struct shash_desc *sha3_desc; unsigned int digestsize; size_t i; int retval; sha3_tfm = crypto_alloc_shash("sha3-512", 0, 0); if (IS_ERR_OR_NULL(sha3_tfm)) { pr_err("Could not allocate hash tfm: %ld\n", PTR_ERR(sha3_tfm)); return PTR_ERR(sha3_tfm); } digestsize = crypto_shash_digestsize(sha3_tfm); if (*digest_len < digestsize) { pr_err("Digest buffer too small, need at least %u bytes\n", digestsize); retval = -EINVAL; goto out; } sha3_desc = kzalloc(sizeof(*sha3_desc) + crypto_shash_descsize(sha3_tfm), GFP_KERNEL); if (!sha3_desc) { pr_err("Could not allocate hash desc\n"); retval = -ENOMEM; goto out; } sha3_desc->tfm = sha3_tfm; sha3_desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; retval = crypto_shash_digest(sha3_desc, input, input_len, digest); *digest_len = digestsize; pr_info("Hashed %zu bytes, digest = ", input_len); for (i = 0; i < digestsize; i++) pr_cont("%02x", digest[i]); pr_info("\n"); kfree(sha3_desc); out: crypto_free_shash(sha3_tfm); return retval; } /** * pwkeeper_master_write() - callback invoked when a process writes to * /dev/pwkeeper_master * @filp: process's file object that is writing to this device (ignored) * @ubuf: source buffer from user * @count: number of bytes in @ubuf * @ppos: file offset (in/out parameter) * * If *@ppos does not point to zero, do nothing and return -EINVAL. * * Copy the contents of @ubuf to the master password for the user, the * lesser of @count and MASTERPW_LEN. Then increment the value pointed * to by @ppos by the number of bytes copied. * * When replacing an existing master password, recalculate all account * passwords. * * Caution: @ubuf is not a string; it is not null-terminated. * * Return: number of bytes copied from @ubuf, or negative on error */ static ssize_t pwkeeper_master_write(struct file *filp, const char __user * ubuf, size_t count, loff_t * ppos) { /* Part 2: YOUR CODE HERE */ return -EPERM; } /** * pwkeeper_account_read() - callback invoked when a process reads * from /dev/pwkeeper_account * @filp: process's file object that is reading from this device (ignored) * @ubuf: destination to store account password * @count: number of bytes in @ubuf * @ppos: file offset (in/out parameter) * * Write to @ubuf the password generated for the most recently written * account name for the current UID, offset by @ppos. Copy the lesser * of @count and (ACCOUNTPW_LEN - *@ppos). Then increment the value * pointed to by @ppos by the number of bytes written. If @ppos is * greater than or equal to ACCOUNTPW_LEN, then write * nothing. * * If no account name was set (via previous successful invocation of * pwkeeper_account_write()), do nothing and return -ENOKEY. * * Return: number of bytes written to @ubuf, 0 on end of file, or * negative on error */ static ssize_t pwkeeper_account_read(struct file *filp, char __user * ubuf, size_t count, loff_t * ppos) { /* Part 3: YOUR CODE HERE */ return -EPERM; } /** * pwkeeper_account_write() - callback invoked when a process writes * to /dev/pwkeeper_account * @filp: process's file object that is writing to this device (ignored) * @ubuf: source buffer from user * @count: number of bytes in @ubuf * @ppos: file offset (in/out parameter) * * If *@ppos does not point to zero, do nothing and return -EINVAL. * * If the current user has not set a master password, do nothing and * return -ENOKEY. * * Otherwise check if @ubuf is already in the accounts list associated * with the current user. If it is already there, do nothing and * return @count. * * Otherwise, create a new node in the accounts list associated with * the current user. Copy the contents of @ubuf to that node, the * lesser of @count and ACCOUNTNAME_LEN. Increment the value pointed * to by @ppos by the number of bytes copied. Finally, perform the key * derivation function as specified in the project description, to * determine the account's password. * * Caution: @ubuf is not a string; it is not null-terminated. * * Return: @count, or negative on error */ static ssize_t pwkeeper_account_write(struct file *filp, const char __user * ubuf, size_t count, loff_t * ppos) { /* Part 3: YOUR CODE HERE */ return -EPERM; } static const struct file_operations pwkeeper_master_fops = { .write = pwkeeper_master_write, }; static struct miscdevice pwkeeper_master_dev = { .minor = MISC_DYNAMIC_MINOR, .name = "pwkeeper_master", .fops = &pwkeeper_master_fops, .mode = 0666 }; static const struct file_operations pwkeeper_account_fops = { .read = pwkeeper_account_read, .write = pwkeeper_account_write, }; static struct miscdevice pwkeeper_account_dev = { .minor = MISC_DYNAMIC_MINOR, .name = "pwkeeper_account", .fops = &pwkeeper_account_fops, .mode = 0666 }; /** * pwkeeper_accounts_show() - callback invoked when a process reads from * /sys/devices/platform/pwkeeper/accounts * * @dev: device driver data for sysfs entry (ignored) * @attr: sysfs entry context (ignored) * @buf: destination to store current user's accounts * * Write to @buf, up to PAGE_SIZE characters, a human-readable message * that lists all accounts registered for the current UID, and the * associated account passwords. Note that @buf is a normal character * buffer, not a __user buffer. Use scnprintf() in this function. * * @return Number of bytes written to @buf, or negative on error. */ static ssize_t pwkeeper_accounts_show(struct device *dev, struct device_attribute *attr, char *buf) { /* Part 4: YOUR CODE HERE */ return -EPERM; } /** * pwkeeper_master_show() - callback invoked when a process reads from * /sys/devices/platform/pwkeeper/masters * * @dev: device driver data for sysfs entry (ignored) * @attr: sysfs entry context (ignored) * @buf: destination to store login statistics * * Check if the calling process has CAP_SYS_ADMIN. If not, return * -EPERM. * * Otherwise, write to @buf, up to PAGE_SIZE characters, a * human-readable message that lists all users IDs that have * registered master passwords. Note that @buf is a normal character * buffer, not a __user buffer. Use scnprintf() in this function. * * @return Number of bytes written to @buf, or negative on error. */ static ssize_t pwkeeper_masters_show(struct device *dev, struct device_attribute *attr, char *buf) { /* Part 4: YOUR CODE HERE */ return -EPERM; } static DEVICE_ATTR(accounts, S_IRUGO, pwkeeper_accounts_show, NULL); static DEVICE_ATTR(masters, S_IRUGO, pwkeeper_masters_show, NULL); /** * cs421net_top() - top-half of CS421Net ISR * @irq: IRQ that was invoked (ignored) * @cookie: Pointer to data that was passed into * request_threaded_irq() (ignored) * * If @irq is CS421NET_IRQ, then wake up the bottom-half. Otherwise, * return IRQ_NONE. */ static irqreturn_t cs421net_top(int irq, void *cookie) { /* Part 5: YOUR CODE HERE */ return IRQ_NONE; } /** * cs421net_bottom() - bottom-half to CS421Net ISR * @irq: IRQ that was invoked (ignore) * @cookie: Pointer that was passed into request_threaded_irq() * (ignored) * * Fetch the incoming packet, via cs421net_get_data(). Treat the input * as a 32-BIT LITTLE ENDIAN BINARY VALUE representing a UID. Search * through the master list and accounts list, deleting all nodes with * that UID. If the UID is exactly zero, then delete ALL nodes in the * master and accounts lists. * * If the packet length is not exactly 4 bytes, or if the provided * value does not match a registered UID in the master list, then do * nothing. * * Remember to add appropriate spin lock calls in this function. * * Caution: The incoming payload is not a string; it is not null-terminated. * You can NOT use strcpy() or strlen() on it. * * Return: always IRQ_HANDLED */ static irqreturn_t cs421net_bottom(int irq, void *cookie) { /* Part 5: YOUR CODE HERE */ return IRQ_HANDLED; } /** * pwkeeper_probe() - callback invoked when this driver is probed * @pdev platform device driver data (ignored) * * Return: 0 on successful probing, negative on error */ static int pwkeeper_probe(struct platform_device *pdev) { int retval; retval = misc_register(&pwkeeper_master_dev); if (retval) { pr_err("Could not register master device\n"); goto err; } retval = misc_register(&pwkeeper_account_dev); if (retval) { pr_err("Could not register account device\n"); goto err_deregister_master; } retval = device_create_file(&pdev->dev, &dev_attr_accounts); if (retval) { pr_err("Could not create sysfs entry\n"); goto err_deregister_account; } retval = device_create_file(&pdev->dev, &dev_attr_masters); if (retval) { pr_err("Could not create sysfs entry\n"); goto err_remove_sysfs_accounts; } /* * In part 5, register the ISR and enable network * integration. Make sure you clean up upon error. */ /* YOUR CODE HERE */ pr_info("Probe successful\n"); return 0; err_remove_sysfs_masters: device_remove_file(&pdev->dev, &dev_attr_masters); err_remove_sysfs_accounts: device_remove_file(&pdev->dev, &dev_attr_accounts); err_deregister_account: misc_deregister(&pwkeeper_account_dev); err_deregister_master: misc_deregister(&pwkeeper_master_dev); err: pr_err("Probe failed, error %d\n", retval); return retval; } /** * pwkeeper_remove() - callback when this driver is removed * @pdev platform device driver data (ignored) * * Return: Always 0 */ static int pwkeeper_remove(struct platform_device *pdev) { pr_info("Removing\n"); /* * In part 5, disable network integration and remove the ISR. */ /* YOUR CODE HERE */ /* * In part 3, free all memory associated with accounts list. */ /* YOUR CODE HERE */ /* * In part 2, free all memory associated with master password * list. */ /* YOUR CODE HERE */ device_remove_file(&pdev->dev, &dev_attr_masters); device_remove_file(&pdev->dev, &dev_attr_accounts); misc_deregister(&pwkeeper_account_dev); misc_deregister(&pwkeeper_master_dev); return 0; } static struct platform_driver cs421_driver = { .driver = { .name = "pwkeeper", }, .probe = pwkeeper_probe, .remove = pwkeeper_remove, }; static struct platform_device *pdev; /** * cs421_init() - create the platform driver * This is needed so that the device gains a sysfs group. * * You do not need to modify this function. */ static int __init cs421_init(void) { pdev = platform_device_register_simple("pwkeeper", -1, NULL, 0); if (IS_ERR(pdev)) return PTR_ERR(pdev); return platform_driver_register(&cs421_driver); } /** * cs421_exit() - remove the platform driver * Unregister the driver from the platform bus. * * You do not need to modify this function. */ static void __exit cs421_exit(void) { platform_driver_unregister(&cs421_driver); platform_device_unregister(pdev); } module_init(cs421_init); module_exit(cs421_exit); MODULE_DESCRIPTION("CS421 Password Keeper - project 2"); MODULE_LICENSE("GPL");