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 */
34 #include "resolve_host.h"
37 #define THIS_PROGRAM_NAME "cifscreds"
39 /* max length of appropriate command */
40 #define MAX_COMMAND_SIZE 32
49 int (*action)(struct cmdarg *arg);
50 const char name[MAX_COMMAND_SIZE];
54 static int cifscreds_add(struct cmdarg *arg);
55 static int cifscreds_clear(struct cmdarg *arg);
56 static int cifscreds_clearall(struct cmdarg *arg);
57 static int cifscreds_update(struct cmdarg *arg);
59 static const char *thisprogram;
61 static struct command commands[] = {
62 { cifscreds_add, "add", "[-u username] [-d] <host|domain>" },
63 { cifscreds_clear, "clear", "[-u username] [-d] <host|domain>" },
64 { cifscreds_clearall, "clearall", "" },
65 { cifscreds_update, "update", "[-u username] [-d] <host|domain>" },
69 static struct option longopts[] = {
70 {"username", 1, NULL, 'u'},
71 {"domain", 0, NULL, 'd' },
75 /* display usage information */
81 fprintf(stderr, "Usage:\n");
82 for (cmd = commands; cmd->action; cmd++)
83 fprintf(stderr, "\t%s %s %s\n", thisprogram,
84 cmd->name, cmd->format);
85 fprintf(stderr, "\n");
90 /* search all program's keys in keyring */
91 static key_serial_t key_search_all(void)
93 key_serial_t key, *pk;
96 int count, dpos, n, ret;
98 /* read the key payload data */
99 count = keyctl_read_alloc(DEST_KEYRING, &keylist);
103 count /= sizeof(key_serial_t);
107 goto key_search_all_out;
110 /* list the keys in the keyring */
115 ret = keyctl_describe_alloc(key, &buffer);
119 n = sscanf(buffer, "%*[^;];%*d;%*d;%*x;%n", &dpos);
125 if (strstr(buffer + dpos, KEY_PREFIX ":") ==
130 goto key_search_all_out;
143 /* add command handler */
144 static int cifscreds_add(struct cmdarg *arg)
146 char addrstr[MAX_ADDR_LIST_LEN];
147 char *currentaddress, *nextaddress;
151 if (arg->host == NULL || arg->user == NULL)
154 if (arg->keytype == 'd')
155 strlcpy(addrstr, arg->host, MAX_ADDR_LIST_LEN);
157 ret = resolve_host(arg->host, addrstr);
161 fprintf(stderr, "error: Could not resolve address "
162 "for %s\n", arg->host);
166 fprintf(stderr, "error: Problem parsing address list\n");
170 if (strpbrk(arg->user, USER_DISALLOWED_CHARS)) {
171 fprintf(stderr, "error: Incorrect username\n");
175 /* search for same credentials stashed for current host */
176 currentaddress = addrstr;
177 nextaddress = strchr(currentaddress, ',');
179 *nextaddress++ = '\0';
181 while (currentaddress) {
182 if (key_search(currentaddress, arg->keytype) > 0) {
183 printf("You already have stashed credentials "
184 "for %s (%s)\n", currentaddress, arg->host);
185 printf("If you want to update them use:\n");
186 printf("\t%s update\n", thisprogram);
196 printf("Key search failed: %s\n", strerror(errno));
200 currentaddress = nextaddress;
201 if (currentaddress) {
202 *(currentaddress - 1) = ',';
203 nextaddress = strchr(currentaddress, ',');
205 *nextaddress++ = '\0';
210 * if there isn't same credentials stashed add them to keyring
211 * and set permisson mask
213 pass = getpass("Password: ");
215 currentaddress = addrstr;
216 nextaddress = strchr(currentaddress, ',');
218 *nextaddress++ = '\0';
220 while (currentaddress) {
221 key_serial_t key = key_add(currentaddress, arg->user, pass, arg->keytype);
223 fprintf(stderr, "error: Add credential key for %s: %s\n",
224 currentaddress, strerror(errno));
226 if (keyctl(KEYCTL_SETPERM, key, CIFS_KEY_PERMS) < 0) {
227 fprintf(stderr, "error: Setting permissons "
228 "on key, attempt to delete...\n");
230 if (keyctl(KEYCTL_UNLINK, key, DEST_KEYRING) < 0) {
231 fprintf(stderr, "error: Deleting key from "
232 "keyring for %s (%s)\n",
233 currentaddress, arg->host);
238 currentaddress = nextaddress;
239 if (currentaddress) {
240 nextaddress = strchr(currentaddress, ',');
242 *nextaddress++ = '\0';
249 /* clear command handler */
250 static int cifscreds_clear(struct cmdarg *arg)
252 char addrstr[MAX_ADDR_LIST_LEN];
253 char *currentaddress, *nextaddress;
254 int ret = 0, count = 0, errors = 0;
256 if (arg->host == NULL || arg->user == NULL)
259 if (arg->keytype == 'd')
260 strlcpy(addrstr, arg->host, MAX_ADDR_LIST_LEN);
262 ret = resolve_host(arg->host, addrstr);
266 fprintf(stderr, "error: Could not resolve address "
267 "for %s\n", arg->host);
271 fprintf(stderr, "error: Problem parsing address list\n");
275 if (strpbrk(arg->user, USER_DISALLOWED_CHARS)) {
276 fprintf(stderr, "error: Incorrect username\n");
281 * search for same credentials stashed for current host
282 * and unlink them from session keyring
284 currentaddress = addrstr;
285 nextaddress = strchr(currentaddress, ',');
287 *nextaddress++ = '\0';
289 while (currentaddress) {
290 key_serial_t key = key_search(currentaddress, arg->keytype);
292 if (keyctl(KEYCTL_UNLINK, key, DEST_KEYRING) < 0) {
293 fprintf(stderr, "error: Removing key from "
294 "keyring for %s (%s)\n",
295 currentaddress, arg->host);
302 currentaddress = nextaddress;
303 if (currentaddress) {
304 nextaddress = strchr(currentaddress, ',');
306 *nextaddress++ = '\0';
310 if (!count && !errors) {
311 printf("You have no same stashed credentials "
312 " for %s\n", arg->host);
313 printf("If you want to add them use:\n");
314 printf("\t%s add\n", thisprogram);
322 /* clearall command handler */
323 static int cifscreds_clearall(struct cmdarg *arg __attribute__ ((unused)))
326 int count = 0, errors = 0;
329 * search for all program's credentials stashed in session keyring
330 * and then unlink them
333 key = key_search_all();
335 if (keyctl(KEYCTL_UNLINK, key, DEST_KEYRING) < 0) {
336 fprintf(stderr, "error: Deleting key "
345 if (!count && !errors) {
346 printf("You have no stashed " KEY_PREFIX
348 printf("If you want to add them use:\n");
349 printf("\t%s add\n", thisprogram);
357 /* update command handler */
358 static int cifscreds_update(struct cmdarg *arg)
360 char addrstr[MAX_ADDR_LIST_LEN];
361 char *currentaddress, *nextaddress, *pass;
363 int ret = 0, id, count = 0;
365 if (arg->host == NULL || arg->user == NULL)
368 if (arg->keytype == 'd')
369 strlcpy(addrstr, arg->host, MAX_ADDR_LIST_LEN);
371 ret = resolve_host(arg->host, addrstr);
375 fprintf(stderr, "error: Could not resolve address "
376 "for %s\n", arg->host);
380 fprintf(stderr, "error: Problem parsing address list\n");
384 if (strpbrk(arg->user, USER_DISALLOWED_CHARS)) {
385 fprintf(stderr, "error: Incorrect username\n");
389 /* search for necessary credentials stashed in session keyring */
390 currentaddress = addrstr;
391 nextaddress = strchr(currentaddress, ',');
393 *nextaddress++ = '\0';
395 while (currentaddress) {
396 if (key_search(currentaddress, arg->keytype) > 0) {
397 addrs[count] = currentaddress;
401 currentaddress = nextaddress;
402 if (currentaddress) {
403 nextaddress = strchr(currentaddress, ',');
405 *nextaddress++ = '\0';
410 printf("You have no same stashed credentials "
411 "for %s\n", arg->host);
412 printf("If you want to add them use:\n");
413 printf("\t%s add\n", thisprogram);
418 /* update payload of found keys */
419 pass = getpass("Password: ");
421 for (id = 0; id < count; id++) {
422 key_serial_t key = key_add(addrs[id], arg->user, pass, arg->keytype);
424 fprintf(stderr, "error: Update credential key "
425 "for %s: %s\n", addrs[id], strerror(errno));
432 check_session_keyring(void)
434 key_serial_t ses_key, uses_key;
436 ses_key = keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 0);
439 fprintf(stderr, "Error: you have no session keyring. "
440 "Consider using pam_keyinit to "
443 fprintf(stderr, "Error: unable to query session "
444 "keyring: %s\n", strerror(errno));
448 /* A problem querying the user-session keyring isn't fatal. */
449 uses_key = keyctl_get_keyring_ID(KEY_SPEC_USER_SESSION_KEYRING, 0);
453 if (ses_key == uses_key)
454 fprintf(stderr, "Warning: you have no persistent session "
455 "keyring. cifscreds keys will not persist "
456 "after this process exits. See "
457 "pam_keyinit(8).\n");
461 int main(int argc, char **argv)
463 struct command *cmd, *best;
467 memset(&arg, 0, sizeof(arg));
470 thisprogram = (char *)basename(argv[0]);
471 if (thisprogram == NULL)
472 thisprogram = THIS_PROGRAM_NAME;
477 while((n = getopt_long(argc, argv, "du:", longopts, NULL)) != -1) {
480 arg.keytype = (char) n;
493 /* find the best fit command */
495 n = strnlen(argv[optind], MAX_COMMAND_SIZE);
497 for (cmd = commands; cmd->action; cmd++) {
498 if (memcmp(cmd->name, argv[optind], n) != 0)
501 if (cmd->name[n] == 0) {
509 fprintf(stderr, "Ambiguous command\n");
517 fprintf(stderr, "Unknown command\n");
521 /* second argument should be host or domain */
523 arg.host = argv[optind + 1];
525 if (arg.host && arg.keytype == 'd' &&
526 strpbrk(arg.host, DOMAIN_DISALLOWED_CHARS)) {
527 fprintf(stderr, "error: Domain name contains invalid characters\n");
531 if (arg.user == NULL)
532 arg.user = getusername(getuid());
534 if (check_session_keyring())
537 return best->action(&arg);