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 */
33 #include "resolve_host.h"
36 #define THIS_PROGRAM_NAME "cifscreds"
37 #define KEY_PREFIX "cifs"
39 /* max length of appropriate command */
40 #define MAX_COMMAND_SIZE 32
42 /* max length of username, password and domain name */
43 #define MAX_USERNAME_SIZE 32
44 #define MOUNT_PASSWD_SIZE 128
45 #define MAX_DOMAIN_SIZE 64
48 * disallowed characters for user and domain names. See:
49 * http://technet.microsoft.com/en-us/library/bb726984.aspx
50 * http://support.microsoft.com/kb/909264
52 #define USER_DISALLOWED_CHARS "\\/\"[]:|<>+=;,?*"
53 #define DOMAIN_DISALLOWED_CHARS "\\/:*?\"<>|"
55 /* destination keyring */
56 #define DEST_KEYRING KEY_SPEC_SESSION_KEYRING
57 #define CIFS_KEY_TYPE "logon"
58 #define CIFS_KEY_PERMS (KEY_POS_VIEW|KEY_POS_WRITE|KEY_POS_SEARCH| \
59 KEY_USR_VIEW|KEY_USR_WRITE|KEY_USR_SEARCH)
68 int (*action)(struct cmdarg *arg);
69 const char name[MAX_COMMAND_SIZE];
73 static int cifscreds_add(struct cmdarg *arg);
74 static int cifscreds_clear(struct cmdarg *arg);
75 static int cifscreds_clearall(struct cmdarg *arg);
76 static int cifscreds_update(struct cmdarg *arg);
78 const char *thisprogram;
80 struct command commands[] = {
81 { cifscreds_add, "add", "[-u username] [-d] <host|domain>" },
82 { cifscreds_clear, "clear", "[-u username] [-d] <host|domain>" },
83 { cifscreds_clearall, "clearall", "" },
84 { cifscreds_update, "update", "[-u username] [-d] <host|domain>" },
88 struct option longopts[] = {
89 {"username", 1, NULL, 'u'},
90 {"domain", 0, NULL, 'd' },
94 /* display usage information */
100 fprintf(stderr, "Usage:\n");
101 for (cmd = commands; cmd->action; cmd++)
102 fprintf(stderr, "\t%s %s %s\n", thisprogram,
103 cmd->name, cmd->format);
104 fprintf(stderr, "\n");
109 /* search a specific key in keyring */
111 key_search(const char *addr, char keytype)
113 char desc[INET6_ADDRSTRLEN + sizeof(KEY_PREFIX) + 4];
115 sprintf(desc, "%s:%c:%s", KEY_PREFIX, keytype, addr);
117 return keyctl_search(DEST_KEYRING, CIFS_KEY_TYPE, desc, 0);
120 /* search all program's keys in keyring */
121 static key_serial_t key_search_all(void)
123 key_serial_t key, *pk;
126 int count, dpos, n, ret;
128 /* read the key payload data */
129 count = keyctl_read_alloc(DEST_KEYRING, &keylist);
133 count /= sizeof(key_serial_t);
137 goto key_search_all_out;
140 /* list the keys in the keyring */
145 ret = keyctl_describe_alloc(key, &buffer);
149 n = sscanf(buffer, "%*[^;];%*d;%*d;%*x;%n", &dpos);
155 if (strstr(buffer + dpos, KEY_PREFIX ":") ==
160 goto key_search_all_out;
173 /* add or update a specific key to keyring */
175 key_add(const char *addr, const char *user, const char *pass, char keytype)
178 char desc[INET6_ADDRSTRLEN + sizeof(KEY_PREFIX) + 4];
179 char val[MOUNT_PASSWD_SIZE + MAX_USERNAME_SIZE + 2];
181 /* set key description */
182 sprintf(desc, "%s:%c:%s", KEY_PREFIX, keytype, addr);
184 /* set payload contents */
185 len = sprintf(val, "%s:%s", user, pass);
187 return add_key(CIFS_KEY_TYPE, desc, val, len + 1, DEST_KEYRING);
190 /* add command handler */
191 static int cifscreds_add(struct cmdarg *arg)
193 char addrstr[MAX_ADDR_LIST_LEN];
194 char *currentaddress, *nextaddress;
198 if (arg->host == NULL || arg->user == NULL)
201 if (arg->keytype == 'd')
202 strlcpy(addrstr, arg->host, MAX_ADDR_LIST_LEN);
204 ret = resolve_host(arg->host, addrstr);
208 fprintf(stderr, "error: Could not resolve address "
209 "for %s\n", arg->host);
213 fprintf(stderr, "error: Problem parsing address list\n");
217 if (strpbrk(arg->user, USER_DISALLOWED_CHARS)) {
218 fprintf(stderr, "error: Incorrect username\n");
222 /* search for same credentials stashed for current host */
223 currentaddress = addrstr;
224 nextaddress = strchr(currentaddress, ',');
226 *nextaddress++ = '\0';
228 while (currentaddress) {
229 if (key_search(currentaddress, arg->keytype) > 0) {
230 printf("You already have stashed credentials "
231 "for %s (%s)\n", currentaddress, arg->host);
232 printf("If you want to update them use:\n");
233 printf("\t%s update\n", thisprogram);
238 currentaddress = nextaddress;
239 if (currentaddress) {
240 *(currentaddress - 1) = ',';
241 nextaddress = strchr(currentaddress, ',');
243 *nextaddress++ = '\0';
248 * if there isn't same credentials stashed add them to keyring
249 * and set permisson mask
251 pass = getpass("Password: ");
253 currentaddress = addrstr;
254 nextaddress = strchr(currentaddress, ',');
256 *nextaddress++ = '\0';
258 while (currentaddress) {
259 key_serial_t key = key_add(currentaddress, arg->user, pass, arg->keytype);
261 fprintf(stderr, "error: Add credential key for %s\n",
264 if (keyctl(KEYCTL_SETPERM, key, CIFS_KEY_PERMS) < 0) {
265 fprintf(stderr, "error: Setting permissons "
266 "on key, attempt to delete...\n");
268 if (keyctl(KEYCTL_UNLINK, key, DEST_KEYRING) < 0) {
269 fprintf(stderr, "error: Deleting key from "
270 "keyring for %s (%s)\n",
271 currentaddress, arg->host);
276 currentaddress = nextaddress;
277 if (currentaddress) {
278 nextaddress = strchr(currentaddress, ',');
280 *nextaddress++ = '\0';
287 /* clear command handler */
288 static int cifscreds_clear(struct cmdarg *arg)
290 char addrstr[MAX_ADDR_LIST_LEN];
291 char *currentaddress, *nextaddress;
292 int ret = 0, count = 0, errors = 0;
294 if (arg->host == NULL || arg->user == NULL)
297 if (arg->keytype == 'd')
298 strlcpy(addrstr, arg->host, MAX_ADDR_LIST_LEN);
300 ret = resolve_host(arg->host, addrstr);
304 fprintf(stderr, "error: Could not resolve address "
305 "for %s\n", arg->host);
309 fprintf(stderr, "error: Problem parsing address list\n");
313 if (strpbrk(arg->user, USER_DISALLOWED_CHARS)) {
314 fprintf(stderr, "error: Incorrect username\n");
319 * search for same credentials stashed for current host
320 * and unlink them from session keyring
322 currentaddress = addrstr;
323 nextaddress = strchr(currentaddress, ',');
325 *nextaddress++ = '\0';
327 while (currentaddress) {
328 key_serial_t key = key_search(currentaddress, arg->keytype);
330 if (keyctl(KEYCTL_UNLINK, key, DEST_KEYRING) < 0) {
331 fprintf(stderr, "error: Removing key from "
332 "keyring for %s (%s)\n",
333 currentaddress, arg->host);
340 currentaddress = nextaddress;
341 if (currentaddress) {
342 nextaddress = strchr(currentaddress, ',');
344 *nextaddress++ = '\0';
348 if (!count && !errors) {
349 printf("You have no same stashed credentials "
350 " for %s\n", arg->host);
351 printf("If you want to add them use:\n");
352 printf("\t%s add\n", thisprogram);
360 /* clearall command handler */
361 static int cifscreds_clearall(struct cmdarg *arg __attribute__ ((unused)))
364 int count = 0, errors = 0;
367 * search for all program's credentials stashed in session keyring
368 * and then unlink them
371 key = key_search_all();
373 if (keyctl(KEYCTL_UNLINK, key, DEST_KEYRING) < 0) {
374 fprintf(stderr, "error: Deleting key "
383 if (!count && !errors) {
384 printf("You have no stashed " KEY_PREFIX
386 printf("If you want to add them use:\n");
387 printf("\t%s add\n", thisprogram);
395 /* update command handler */
396 static int cifscreds_update(struct cmdarg *arg)
398 char addrstr[MAX_ADDR_LIST_LEN];
399 char *currentaddress, *nextaddress, *pass;
401 int ret = 0, id, count = 0;
403 if (arg->host == NULL || arg->user == NULL)
406 if (arg->keytype == 'd')
407 strlcpy(addrstr, arg->host, MAX_ADDR_LIST_LEN);
409 ret = resolve_host(arg->host, addrstr);
413 fprintf(stderr, "error: Could not resolve address "
414 "for %s\n", arg->host);
418 fprintf(stderr, "error: Problem parsing address list\n");
422 if (strpbrk(arg->user, USER_DISALLOWED_CHARS)) {
423 fprintf(stderr, "error: Incorrect username\n");
427 /* search for necessary credentials stashed in session keyring */
428 currentaddress = addrstr;
429 nextaddress = strchr(currentaddress, ',');
431 *nextaddress++ = '\0';
433 while (currentaddress) {
434 if (key_search(currentaddress, arg->keytype) > 0) {
435 addrs[count] = currentaddress;
439 currentaddress = nextaddress;
440 if (currentaddress) {
441 nextaddress = strchr(currentaddress, ',');
443 *nextaddress++ = '\0';
448 printf("You have no same stashed credentials "
449 "for %s\n", arg->host);
450 printf("If you want to add them use:\n");
451 printf("\t%s add\n", thisprogram);
456 /* update payload of found keys */
457 pass = getpass("Password: ");
459 for (id = 0; id < count; id++) {
460 key_serial_t key = key_add(addrs[id], arg->user, pass, arg->keytype);
462 fprintf(stderr, "error: Update credential key "
463 "for %s\n", addrs[id]);
470 check_session_keyring(void)
472 key_serial_t ses_key, uses_key;
474 ses_key = keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 0);
477 fprintf(stderr, "Error: you have no session keyring. "
478 "Consider using pam_keyinit to "
481 fprintf(stderr, "Error: unable to query session "
482 "keyring: %s\n", strerror(errno));
486 /* A problem querying the user-session keyring isn't fatal. */
487 uses_key = keyctl_get_keyring_ID(KEY_SPEC_USER_SESSION_KEYRING, 0);
491 if (ses_key == uses_key)
492 fprintf(stderr, "Warning: you have no persistent session "
493 "keyring. cifscreds keys will not persist "
494 "after this process exits. See "
495 "pam_keyinit(8).\n");
499 int main(int argc, char **argv)
501 struct command *cmd, *best;
505 memset(&arg, 0, sizeof(arg));
508 thisprogram = (char *)basename(argv[0]);
509 if (thisprogram == NULL)
510 thisprogram = THIS_PROGRAM_NAME;
515 while((n = getopt_long(argc, argv, "du:", longopts, NULL)) != -1) {
518 arg.keytype = (char) n;
528 /* find the best fit command */
530 n = strnlen(argv[optind], MAX_COMMAND_SIZE);
532 for (cmd = commands; cmd->action; cmd++) {
533 if (memcmp(cmd->name, argv[optind], n) != 0)
536 if (cmd->name[n] == 0) {
544 fprintf(stderr, "Ambiguous command\n");
552 fprintf(stderr, "Unknown command\n");
556 /* second argument should be host or domain */
558 arg.host = argv[optind + 1];
560 if (arg.host && arg.keytype == 'd' &&
561 strpbrk(arg.host, DOMAIN_DISALLOWED_CHARS)) {
562 fprintf(stderr, "error: Domain name contains invalid characters\n");
566 if (arg.user == NULL)
567 arg.user = getusername(getuid());
569 if (check_session_keyring())
572 return best->action(&arg);