2 * Credentials stashing utility for Linux CIFS VFS (virtual filesystem) client
3 * Copyright (C) 2010 Jeff Layton (jlayton@samba.org)
4 * Copyright (C) 2010 Igor Druzhinin (jaxbrigs@gmail.com)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #endif /* HAVE_CONFIG_H */
31 #include "resolve_host.h"
33 #define THIS_PROGRAM_NAME "cifscreds"
35 /* max length of appropriate command */
36 #define MAX_COMMAND_SIZE 32
38 /* max length of username, password and domain name */
39 #define MAX_USERNAME_SIZE 32
40 #define MOUNT_PASSWD_SIZE 128
41 #define MAX_DOMAIN_SIZE 64
43 /* allowed and disallowed characters for user and domain name */
44 #define USER_DISALLOWED_CHARS "\\/\"[]:|<>+=;,?*@"
45 #define DOMAIN_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz" \
46 "ABCDEFGHIJKLMNOPQRSTUVWXYZ-."
48 /* destination keyring */
49 #define DEST_KEYRING KEY_SPEC_USER_KEYRING
52 int (*action)(int argc, char *argv[]);
53 const char name[MAX_COMMAND_SIZE];
57 static int cifscreds_add(int argc, char *argv[]);
58 static int cifscreds_clear(int argc, char *argv[]);
59 static int cifscreds_clearall(int argc, char *argv[]);
60 static int cifscreds_update(int argc, char *argv[]);
62 const char *thisprogram;
64 struct command commands[] = {
65 { cifscreds_add, "add", "<host> <user> [domain]" },
66 { cifscreds_clear, "clear", "<host> <user> [domain]" },
67 { cifscreds_clearall, "clearall", "" },
68 { cifscreds_update, "update", "<host> <user> [domain]" },
72 /* display usage information */
73 static void usage(void)
77 fprintf(stderr, "Usage:\n");
78 for (cmd = commands; cmd->action; cmd++)
79 fprintf(stderr, "\t%s %s %s\n", thisprogram,
80 cmd->name, cmd->format);
81 fprintf(stderr, "\n");
86 /* create key's description string from given credentials */
88 create_description(const char *addr, const char *user,
89 const char *domain, char *desc)
94 sprintf(desc, "%s:%s:%s:", THIS_PROGRAM_NAME, addr, user);
97 str_end = desc + strnlen(desc, INET6_ADDRSTRLEN + \
98 + MAX_USERNAME_SIZE + \
99 + sizeof(THIS_PROGRAM_NAME) + 3);
100 str_len = strnlen(domain, MAX_DOMAIN_SIZE);
102 *str_end = tolower(*domain++);
111 /* search a specific key in keyring */
113 key_search(const char *addr, const char *user, const char *domain)
115 char desc[INET6_ADDRSTRLEN + MAX_USERNAME_SIZE + MAX_DOMAIN_SIZE + \
116 + sizeof(THIS_PROGRAM_NAME) + 3];
117 key_serial_t key, *pk;
120 int count, dpos, n, ret;
122 create_description(addr, user, domain, desc);
124 /* read the key payload data */
125 count = keyctl_read_alloc(DEST_KEYRING, &keylist);
129 count /= sizeof(key_serial_t);
136 /* list the keys in the keyring */
141 ret = keyctl_describe_alloc(key, &buffer);
145 n = sscanf(buffer, "%*[^;];%*d;%*d;%*x;%n", &dpos);
151 if (!strcmp(buffer + dpos, desc)) {
167 /* search all program's keys in keyring */
168 static key_serial_t key_search_all(void)
170 key_serial_t key, *pk;
173 int count, dpos, n, ret;
175 /* read the key payload data */
176 count = keyctl_read_alloc(DEST_KEYRING, &keylist);
180 count /= sizeof(key_serial_t);
184 goto key_search_all_out;
187 /* list the keys in the keyring */
192 ret = keyctl_describe_alloc(key, &buffer);
196 n = sscanf(buffer, "%*[^;];%*d;%*d;%*x;%n", &dpos);
202 if (strstr(buffer + dpos, THIS_PROGRAM_NAME ":") ==
207 goto key_search_all_out;
220 /* add or update a specific key to keyring */
222 key_add(const char *addr, const char *user,
223 const char *domain, const char *pass)
225 char desc[INET6_ADDRSTRLEN + MAX_USERNAME_SIZE + MAX_DOMAIN_SIZE + \
226 + sizeof(THIS_PROGRAM_NAME) + 3];
228 create_description(addr, user, domain, desc);
230 return add_key("user", desc, pass, strnlen(pass, MOUNT_PASSWD_SIZE) + 1,
234 /* add command handler */
235 static int cifscreds_add(int argc, char *argv[])
237 char addrstr[MAX_ADDR_LIST_LEN];
238 char *currentaddress, *nextaddress;
242 if (argc != 4 && argc != 5)
245 ret = resolve_host(argv[2], addrstr);
248 fprintf(stderr, "error: Could not resolve address "
249 "for %s\n", argv[2]);
253 fprintf(stderr, "error: Problem parsing address list\n");
257 if (strpbrk(argv[3], USER_DISALLOWED_CHARS)) {
258 fprintf(stderr, "error: Incorrect username\n");
263 if (strspn(argv[4], DOMAIN_ALLOWED_CHARS) !=
264 strnlen(argv[4], MAX_DOMAIN_SIZE)
266 fprintf(stderr, "error: Incorrect domain name\n");
271 /* search for same credentials stashed for current host */
272 currentaddress = addrstr;
273 nextaddress = strchr(currentaddress, ',');
275 *nextaddress++ = '\0';
277 while (currentaddress) {
278 if (key_search(currentaddress, argv[3],
279 argc == 5 ? argv[4] : NULL) > 0
281 printf("You already have stashed credentials "
282 "for %s (%s)\n", currentaddress, argv[2]);
283 printf("If you want to update them use:\n");
284 printf("\t%s update\n", thisprogram);
289 currentaddress = nextaddress;
290 if (currentaddress) {
291 *(currentaddress - 1) = ',';
292 nextaddress = strchr(currentaddress, ',');
294 *nextaddress++ = '\0';
299 * if there isn't same credentials stashed add them to keyring
300 * and set permisson mask
302 pass = getpass("Password: ");
304 currentaddress = addrstr;
305 nextaddress = strchr(currentaddress, ',');
307 *nextaddress++ = '\0';
309 while (currentaddress) {
310 key_serial_t key = key_add(currentaddress, argv[3],
311 argc == 5 ? argv[4] : NULL, pass);
313 fprintf(stderr, "error: Add credential key for %s\n",
316 if (keyctl(KEYCTL_SETPERM, key, KEY_POS_VIEW | \
317 KEY_POS_WRITE | KEY_USR_VIEW | \
320 fprintf(stderr, "error: Setting permissons "
321 "on key, attempt to delete...\n");
323 if (keyctl(KEYCTL_UNLINK, key, DEST_KEYRING) < 0) {
324 fprintf(stderr, "error: Deleting key from "
325 "keyring for %s (%s)\n",
326 currentaddress, argv[2]);
331 currentaddress = nextaddress;
332 if (currentaddress) {
333 nextaddress = strchr(currentaddress, ',');
335 *nextaddress++ = '\0';
342 /* clear command handler */
343 static int cifscreds_clear(int argc, char *argv[])
345 char addrstr[MAX_ADDR_LIST_LEN];
346 char *currentaddress, *nextaddress;
347 int ret, count = 0, errors = 0;
349 if (argc != 4 && argc != 5)
352 ret = resolve_host(argv[2], addrstr);
355 fprintf(stderr, "error: Could not resolve address "
356 "for %s\n", argv[2]);
360 fprintf(stderr, "error: Problem parsing address list\n");
364 if (strpbrk(argv[3], USER_DISALLOWED_CHARS)) {
365 fprintf(stderr, "error: Incorrect username\n");
370 if (strspn(argv[4], DOMAIN_ALLOWED_CHARS) !=
371 strnlen(argv[4], MAX_DOMAIN_SIZE)
373 fprintf(stderr, "error: Incorrect domain name\n");
379 * search for same credentials stashed for current host
380 * and unlink them from session keyring
382 currentaddress = addrstr;
383 nextaddress = strchr(currentaddress, ',');
385 *nextaddress++ = '\0';
387 while (currentaddress) {
388 key_serial_t key = key_search(currentaddress, argv[3],
389 argc == 5 ? argv[4] : NULL);
391 if (keyctl(KEYCTL_UNLINK, key, DEST_KEYRING) < 0) {
392 fprintf(stderr, "error: Removing key from "
393 "keyring for %s (%s)\n",
394 currentaddress, argv[2]);
401 currentaddress = nextaddress;
402 if (currentaddress) {
403 nextaddress = strchr(currentaddress, ',');
405 *nextaddress++ = '\0';
409 if (!count && !errors) {
410 printf("You have no same stashed credentials "
411 " for %s\n", argv[2]);
412 printf("If you want to add them use:\n");
413 printf("\t%s add\n", thisprogram);
421 /* clearall command handler */
422 static int cifscreds_clearall(int argc, char *argv[] __attribute__ ((unused)))
425 int count = 0, errors = 0;
431 * search for all program's credentials stashed in session keyring
432 * and then unlink them
435 key = key_search_all();
437 if (keyctl(KEYCTL_UNLINK, key, DEST_KEYRING) < 0) {
438 fprintf(stderr, "error: Deleting key "
447 if (!count && !errors) {
448 printf("You have no stashed " THIS_PROGRAM_NAME
450 printf("If you want to add them use:\n");
451 printf("\t%s add\n", thisprogram);
459 /* update command handler */
460 static int cifscreds_update(int argc, char *argv[])
462 char addrstr[MAX_ADDR_LIST_LEN];
463 char *currentaddress, *nextaddress, *pass;
465 int ret, id, count = 0;
467 if (argc != 4 && argc != 5)
470 ret = resolve_host(argv[2], addrstr);
473 fprintf(stderr, "error: Could not resolve address "
474 "for %s\n", argv[2]);
478 fprintf(stderr, "error: Problem parsing address list\n");
482 if (strpbrk(argv[3], USER_DISALLOWED_CHARS)) {
483 fprintf(stderr, "error: Incorrect username\n");
488 if (strspn(argv[4], DOMAIN_ALLOWED_CHARS) !=
489 strnlen(argv[4], MAX_DOMAIN_SIZE)
491 fprintf(stderr, "error: Incorrect domain name\n");
496 /* search for necessary credentials stashed in session keyring */
497 currentaddress = addrstr;
498 nextaddress = strchr(currentaddress, ',');
500 *nextaddress++ = '\0';
502 while (currentaddress) {
503 if (key_search(currentaddress, argv[3],
504 argc == 5 ? argv[4] : NULL) > 0
506 addrs[count] = currentaddress;
510 currentaddress = nextaddress;
511 if (currentaddress) {
512 nextaddress = strchr(currentaddress, ',');
514 *nextaddress++ = '\0';
519 printf("You have no same stashed credentials "
520 "for %s\n", argv[2]);
521 printf("If you want to add them use:\n");
522 printf("\t%s add\n", thisprogram);
527 /* update payload of found keys */
528 pass = getpass("Password: ");
530 for (id = 0; id < count; id++) {
531 key_serial_t key = key_add(addrs[id], argv[3],
532 argc == 5 ? argv[4] : NULL, pass);
534 fprintf(stderr, "error: Update credential key "
535 "for %s\n", addrs[id]);
541 int main(int argc, char **argv)
543 struct command *cmd, *best;
546 thisprogram = (char *)basename(argv[0]);
547 if (thisprogram == NULL)
548 thisprogram = THIS_PROGRAM_NAME;
553 /* find the best fit command */
555 n = strnlen(argv[1], MAX_COMMAND_SIZE);
557 for (cmd = commands; cmd->action; cmd++) {
558 if (memcmp(cmd->name, argv[1], n) != 0)
561 if (cmd->name[n] == 0) {
569 fprintf(stderr, "Ambiguous command\n");
577 fprintf(stderr, "Unknown command\n");
581 exit(best->action(argc, argv));