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 */
32 #include "resolve_host.h"
35 #define THIS_PROGRAM_NAME "cifscreds"
36 #define KEY_PREFIX "cifs"
38 /* max length of appropriate command */
39 #define MAX_COMMAND_SIZE 32
41 /* max length of username, password and domain name */
42 #define MAX_USERNAME_SIZE 32
43 #define MOUNT_PASSWD_SIZE 128
44 #define MAX_DOMAIN_SIZE 64
47 * disallowed characters for user and domain names. See:
48 * http://technet.microsoft.com/en-us/library/bb726984.aspx
49 * http://support.microsoft.com/kb/909264
51 #define USER_DISALLOWED_CHARS "\\/\"[]:|<>+=;,?*"
52 #define DOMAIN_DISALLOWED_CHARS "\\/:*?\"<>|"
54 /* destination keyring */
55 #define DEST_KEYRING KEY_SPEC_SESSION_KEYRING
56 #define CIFS_KEY_TYPE "logon"
57 #define CIFS_KEY_PERMS (KEY_POS_VIEW|KEY_POS_WRITE|KEY_POS_SEARCH| \
58 KEY_USR_VIEW|KEY_USR_WRITE|KEY_USR_SEARCH)
67 int (*action)(struct cmdarg *arg);
68 const char name[MAX_COMMAND_SIZE];
72 static int cifscreds_add(struct cmdarg *arg);
73 static int cifscreds_clear(struct cmdarg *arg);
74 static int cifscreds_clearall(struct cmdarg *arg);
75 static int cifscreds_update(struct cmdarg *arg);
77 const char *thisprogram;
79 struct command commands[] = {
80 { cifscreds_add, "add", "[-u username] [-d] <host|domain>" },
81 { cifscreds_clear, "clear", "[-u username] [-d] <host|domain>" },
82 { cifscreds_clearall, "clearall", "" },
83 { cifscreds_update, "update", "[-u username] [-d] <host|domain>" },
87 struct option longopts[] = {
88 {"username", 1, NULL, 'u'},
89 {"domain", 0, NULL, 'd' },
93 /* display usage information */
99 fprintf(stderr, "Usage:\n");
100 for (cmd = commands; cmd->action; cmd++)
101 fprintf(stderr, "\t%s %s %s\n", thisprogram,
102 cmd->name, cmd->format);
103 fprintf(stderr, "\n");
108 /* search a specific key in keyring */
110 key_search(const char *addr, char keytype)
112 char desc[INET6_ADDRSTRLEN + sizeof(KEY_PREFIX) + 4];
114 sprintf(desc, "%s:%c:%s", KEY_PREFIX, keytype, addr);
116 return keyctl_search(DEST_KEYRING, CIFS_KEY_TYPE, desc, 0);
119 /* search all program's keys in keyring */
120 static key_serial_t key_search_all(void)
122 key_serial_t key, *pk;
125 int count, dpos, n, ret;
127 /* read the key payload data */
128 count = keyctl_read_alloc(DEST_KEYRING, &keylist);
132 count /= sizeof(key_serial_t);
136 goto key_search_all_out;
139 /* list the keys in the keyring */
144 ret = keyctl_describe_alloc(key, &buffer);
148 n = sscanf(buffer, "%*[^;];%*d;%*d;%*x;%n", &dpos);
154 if (strstr(buffer + dpos, KEY_PREFIX ":") ==
159 goto key_search_all_out;
172 /* add or update a specific key to keyring */
174 key_add(const char *addr, const char *user, const char *pass, char keytype)
177 char desc[INET6_ADDRSTRLEN + sizeof(KEY_PREFIX) + 4];
178 char val[MOUNT_PASSWD_SIZE + MAX_USERNAME_SIZE + 2];
180 /* set key description */
181 sprintf(desc, "%s:%c:%s", KEY_PREFIX, keytype, addr);
183 /* set payload contents */
184 len = sprintf(val, "%s:%s", user, pass);
186 return add_key(CIFS_KEY_TYPE, desc, val, len + 1, DEST_KEYRING);
189 /* add command handler */
190 static int cifscreds_add(struct cmdarg *arg)
192 char addrstr[MAX_ADDR_LIST_LEN];
193 char *currentaddress, *nextaddress;
197 if (arg->host == NULL || arg->user == NULL)
200 if (arg->keytype == 'd')
201 strlcpy(addrstr, arg->host, MAX_ADDR_LIST_LEN);
203 ret = resolve_host(arg->host, addrstr);
207 fprintf(stderr, "error: Could not resolve address "
208 "for %s\n", arg->host);
212 fprintf(stderr, "error: Problem parsing address list\n");
216 if (strpbrk(arg->user, USER_DISALLOWED_CHARS)) {
217 fprintf(stderr, "error: Incorrect username\n");
221 /* search for same credentials stashed for current host */
222 currentaddress = addrstr;
223 nextaddress = strchr(currentaddress, ',');
225 *nextaddress++ = '\0';
227 while (currentaddress) {
228 if (key_search(currentaddress, arg->keytype) > 0) {
229 printf("You already have stashed credentials "
230 "for %s (%s)\n", currentaddress, arg->host);
231 printf("If you want to update them use:\n");
232 printf("\t%s update\n", thisprogram);
237 currentaddress = nextaddress;
238 if (currentaddress) {
239 *(currentaddress - 1) = ',';
240 nextaddress = strchr(currentaddress, ',');
242 *nextaddress++ = '\0';
247 * if there isn't same credentials stashed add them to keyring
248 * and set permisson mask
250 pass = getpass("Password: ");
252 currentaddress = addrstr;
253 nextaddress = strchr(currentaddress, ',');
255 *nextaddress++ = '\0';
257 while (currentaddress) {
258 key_serial_t key = key_add(currentaddress, arg->user, pass, arg->keytype);
260 fprintf(stderr, "error: Add credential key for %s\n",
263 if (keyctl(KEYCTL_SETPERM, key, CIFS_KEY_PERMS) < 0) {
264 fprintf(stderr, "error: Setting permissons "
265 "on key, attempt to delete...\n");
267 if (keyctl(KEYCTL_UNLINK, key, DEST_KEYRING) < 0) {
268 fprintf(stderr, "error: Deleting key from "
269 "keyring for %s (%s)\n",
270 currentaddress, arg->host);
275 currentaddress = nextaddress;
276 if (currentaddress) {
277 nextaddress = strchr(currentaddress, ',');
279 *nextaddress++ = '\0';
286 /* clear command handler */
287 static int cifscreds_clear(struct cmdarg *arg)
289 char addrstr[MAX_ADDR_LIST_LEN];
290 char *currentaddress, *nextaddress;
291 int ret = 0, count = 0, errors = 0;
293 if (arg->host == NULL || arg->user == NULL)
296 if (arg->keytype == 'd')
297 strlcpy(addrstr, arg->host, MAX_ADDR_LIST_LEN);
299 ret = resolve_host(arg->host, addrstr);
303 fprintf(stderr, "error: Could not resolve address "
304 "for %s\n", arg->host);
308 fprintf(stderr, "error: Problem parsing address list\n");
312 if (strpbrk(arg->user, USER_DISALLOWED_CHARS)) {
313 fprintf(stderr, "error: Incorrect username\n");
318 * search for same credentials stashed for current host
319 * and unlink them from session keyring
321 currentaddress = addrstr;
322 nextaddress = strchr(currentaddress, ',');
324 *nextaddress++ = '\0';
326 while (currentaddress) {
327 key_serial_t key = key_search(currentaddress, arg->keytype);
329 if (keyctl(KEYCTL_UNLINK, key, DEST_KEYRING) < 0) {
330 fprintf(stderr, "error: Removing key from "
331 "keyring for %s (%s)\n",
332 currentaddress, arg->host);
339 currentaddress = nextaddress;
340 if (currentaddress) {
341 nextaddress = strchr(currentaddress, ',');
343 *nextaddress++ = '\0';
347 if (!count && !errors) {
348 printf("You have no same stashed credentials "
349 " for %s\n", arg->host);
350 printf("If you want to add them use:\n");
351 printf("\t%s add\n", thisprogram);
359 /* clearall command handler */
360 static int cifscreds_clearall(struct cmdarg *arg __attribute__ ((unused)))
363 int count = 0, errors = 0;
366 * search for all program's credentials stashed in session keyring
367 * and then unlink them
370 key = key_search_all();
372 if (keyctl(KEYCTL_UNLINK, key, DEST_KEYRING) < 0) {
373 fprintf(stderr, "error: Deleting key "
382 if (!count && !errors) {
383 printf("You have no stashed " KEY_PREFIX
385 printf("If you want to add them use:\n");
386 printf("\t%s add\n", thisprogram);
394 /* update command handler */
395 static int cifscreds_update(struct cmdarg *arg)
397 char addrstr[MAX_ADDR_LIST_LEN];
398 char *currentaddress, *nextaddress, *pass;
400 int ret = 0, id, count = 0;
402 if (arg->host == NULL || arg->user == NULL)
405 if (arg->keytype == 'd')
406 strlcpy(addrstr, arg->host, MAX_ADDR_LIST_LEN);
408 ret = resolve_host(arg->host, addrstr);
412 fprintf(stderr, "error: Could not resolve address "
413 "for %s\n", arg->host);
417 fprintf(stderr, "error: Problem parsing address list\n");
421 if (strpbrk(arg->user, USER_DISALLOWED_CHARS)) {
422 fprintf(stderr, "error: Incorrect username\n");
426 /* search for necessary credentials stashed in session keyring */
427 currentaddress = addrstr;
428 nextaddress = strchr(currentaddress, ',');
430 *nextaddress++ = '\0';
432 while (currentaddress) {
433 if (key_search(currentaddress, arg->keytype) > 0) {
434 addrs[count] = currentaddress;
438 currentaddress = nextaddress;
439 if (currentaddress) {
440 nextaddress = strchr(currentaddress, ',');
442 *nextaddress++ = '\0';
447 printf("You have no same stashed credentials "
448 "for %s\n", arg->host);
449 printf("If you want to add them use:\n");
450 printf("\t%s add\n", thisprogram);
455 /* update payload of found keys */
456 pass = getpass("Password: ");
458 for (id = 0; id < count; id++) {
459 key_serial_t key = key_add(addrs[id], arg->user, pass, arg->keytype);
461 fprintf(stderr, "error: Update credential key "
462 "for %s\n", addrs[id]);
468 int main(int argc, char **argv)
470 struct command *cmd, *best;
474 memset(&arg, 0, sizeof(arg));
477 thisprogram = (char *)basename(argv[0]);
478 if (thisprogram == NULL)
479 thisprogram = THIS_PROGRAM_NAME;
484 while((n = getopt_long(argc, argv, "du:", longopts, NULL)) != -1) {
487 arg.keytype = (char) n;
497 /* find the best fit command */
499 n = strnlen(argv[optind], MAX_COMMAND_SIZE);
501 for (cmd = commands; cmd->action; cmd++) {
502 if (memcmp(cmd->name, argv[optind], n) != 0)
505 if (cmd->name[n] == 0) {
513 fprintf(stderr, "Ambiguous command\n");
521 fprintf(stderr, "Unknown command\n");
525 /* second argument should be host or domain */
527 arg.host = argv[optind + 1];
529 if (arg.host && arg.keytype == 'd' &&
530 strpbrk(arg.host, DOMAIN_DISALLOWED_CHARS)) {
531 fprintf(stderr, "error: Domain name contains invalid characters\n");
535 if (arg.user == NULL)
536 arg.user = getusername(getuid());
538 return best->action(&arg);