From patchwork Thu Aug 19 20:26:14 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Igor Druzhinin X-Patchwork-Id: 120396 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.4/8.14.3) with ESMTP id o7JKQPuh006102 for ; Thu, 19 Aug 2010 20:26:25 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751467Ab0HSU0Y (ORCPT ); Thu, 19 Aug 2010 16:26:24 -0400 Received: from mail-ey0-f174.google.com ([209.85.215.174]:33837 "EHLO mail-ey0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751445Ab0HSU0X (ORCPT ); Thu, 19 Aug 2010 16:26:23 -0400 Received: by eyg5 with SMTP id 5so1639449eyg.19 for ; Thu, 19 Aug 2010 13:26:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:from:to:cc:subject:date :message-id:x-mailer; bh=0B9Fs1MpGOSK3OHL0GtDnVsbT/po1flMRE5miKMT+yo=; b=ODR32m0ToM0naAmeZ8CrEHAZTQ/AE5sACZ2s51kJhLmGiHrrxwkAp7lmkTe1A3Z6rM t/Af5hrE3w8typGqD6+2YYqDNLyWVzqFXGgEtmDpfAKCLDwo3FZU4vv2kDfYzJvJheIk uncmCzEhMe58Ki/nQkCJkaAA2irJbEJqGziYs= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:to:cc:subject:date:message-id:x-mailer; b=ivtRLR8v+OugDenprLLHwVGx/+yrIL3K8gAmfbtlpSEsCypBcggx0Ps27/OimsD5nw JpWO4M/StAFL4giGiz9jqdYuY0gEfINEMCnxdzWSe3t1nt2DJQ2rk1D89GkKz7P5xLKQ UW8prpe53yi0JUptXmoCDoED91wtG8i2TkTDw= Received: by 10.213.22.5 with SMTP id l5mr1225537ebb.9.1282249582138; Thu, 19 Aug 2010 13:26:22 -0700 (PDT) Received: from localhost.localdomain ([95.84.52.65]) by mx.google.com with ESMTPS id z55sm3314481eeh.9.2010.08.19.13.26.19 (version=TLSv1/SSLv3 cipher=RC4-MD5); Thu, 19 Aug 2010 13:26:21 -0700 (PDT) From: Igor Druzhinin To: jlayton@samba.org Cc: linux-cifs@vger.kernel.org, Igor Druzhinin Subject: [PATCH] cifs-utils: infrastructure for stashing passwords in keyring Date: Fri, 20 Aug 2010 00:26:14 +0400 Message-Id: <1282249574-27724-1-git-send-email-jaxbrigs@gmail.com> X-Mailer: git-send-email 1.7.2.1 Sender: linux-cifs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-cifs@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Thu, 19 Aug 2010 20:26:25 +0000 (UTC) diff --git a/Makefile.am b/Makefile.am index c53c9ec..38a16fe 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,7 +3,7 @@ ACLOCAL_AMFLAGS = -I aclocal root_sbindir = "/sbin" root_sbin_PROGRAMS = mount.cifs -mount_cifs_SOURCES = mount.cifs.c mtab.c util.c +mount_cifs_SOURCES = mount.cifs.c mtab.c resolve_host.c util.c mount_cifs_LDADD = $(LIBCAP) $(CAPNG_LDADD) man_MANS = mount.cifs.8 @@ -15,3 +15,8 @@ cifs_upcall_LDADD = -ltalloc -lkeyutils $(KRB5_LDADD) man_MANS += cifs.upcall.8 endif +if CONFIG_CIFSCREDS +bin_PROGRAMS = cifscreds +cifscreds_SOURCES = cifscreds.c resolve_host.c util.c +cifscreds_LDADD = -lkeyutils +endif diff --git a/cifscreds.c b/cifscreds.c new file mode 100644 index 0000000..f21a47f --- /dev/null +++ b/cifscreds.c @@ -0,0 +1,582 @@ +/* + * Credentials stashing utility for Linux CIFS VFS (virtual filesystem) client + * Copyright (C) 2010 Jeff Layton (jlayton@samba.org) + * Copyright (C) 2010 Igor Druzhinin (jaxbrigs@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include "mount.h" +#include "resolve_host.h" + +#define THIS_PROGRAM_NAME "cifscreds" + +/* max length of appropriate command */ +#define MAX_COMMAND_SIZE 32 + +/* max length of username, password and domain name */ +#define MAX_USERNAME_SIZE 32 +#define MOUNT_PASSWD_SIZE 128 +#define MAX_DOMAIN_SIZE 64 + +/* allowed and disallowed characters for user and domain name */ +#define USER_DISALLOWED_CHARS "\\/\"[]:|<>+=;,?*@" +#define DOMAIN_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ-." + +/* destination keyring */ +#define DEST_KEYRING KEY_SPEC_USER_KEYRING + +struct command { + int (*action)(int argc, char *argv[]); + const char name[MAX_COMMAND_SIZE]; + const char *format; +}; + +static int cifscreds_add(int argc, char *argv[]); +static int cifscreds_clear(int argc, char *argv[]); +static int cifscreds_clearall(int argc, char *argv[]); +static int cifscreds_update(int argc, char *argv[]); + +const char *thisprogram; + +struct command commands[] = { + { cifscreds_add, "add", " [domain]" }, + { cifscreds_clear, "clear", " [domain]" }, + { cifscreds_clearall, "clearall", "" }, + { cifscreds_update, "update", " [domain]" }, + { NULL, "", NULL } +}; + +/* display usage information */ +static void usage(void) +{ + struct command *cmd; + + fprintf(stderr, "Usage:\n"); + for (cmd = commands; cmd->action; cmd++) + fprintf(stderr, "\t%s %s %s\n", thisprogram, + cmd->name, cmd->format); + fprintf(stderr, "\n"); + + exit(EXIT_FAILURE); +} + +/* create key's description string from given credentials */ +static char * +create_description(const char *addr, const char *user, + const char *domain, char *desc) +{ + char *str_end; + int str_len; + + sprintf(desc, "%s:%s:%s:", THIS_PROGRAM_NAME, addr, user); + + if (domain != NULL) { + str_end = desc + strnlen(desc, INET6_ADDRSTRLEN + \ + + MAX_USERNAME_SIZE + \ + + sizeof(THIS_PROGRAM_NAME) + 3); + str_len = strnlen(domain, MAX_DOMAIN_SIZE); + while (str_len--) { + *str_end = tolower(*domain++); + str_end++; + } + *str_end = '\0'; + } + + return desc; +} + +/* search a specific key in keyring */ +static key_serial_t +key_search(const char *addr, const char *user, const char *domain) +{ + char desc[INET6_ADDRSTRLEN + MAX_USERNAME_SIZE + MAX_DOMAIN_SIZE + \ + + sizeof(THIS_PROGRAM_NAME) + 3]; + key_serial_t key, *pk; + void *keylist; + char *buffer; + int count, dpos, n, ret; + + create_description(addr, user, domain, desc); + + /* read the key payload data */ + count = keyctl_read_alloc(DEST_KEYRING, &keylist); + if (count < 0) + return 0; + + count /= sizeof(key_serial_t); + + if (count == 0) { + ret = 0; + goto key_search_out; + } + + /* list the keys in the keyring */ + pk = keylist; + do { + key = *pk++; + + ret = keyctl_describe_alloc(key, &buffer); + if (ret < 0) + continue; + + n = sscanf(buffer, "%*[^;];%*d;%*d;%*x;%n", &dpos); + if (n) { + free(buffer); + continue; + } + + if (!strcmp(buffer + dpos, desc)) { + ret = key; + free(buffer); + goto key_search_out; + } + free(buffer); + + } while (--count); + + ret = 0; + +key_search_out: + free(keylist); + return ret; +} + +/* search all program's keys in keyring */ +static key_serial_t key_search_all(void) +{ + key_serial_t key, *pk; + void *keylist; + char *buffer; + int count, dpos, n, ret; + + /* read the key payload data */ + count = keyctl_read_alloc(DEST_KEYRING, &keylist); + if (count < 0) + return 0; + + count /= sizeof(key_serial_t); + + if (count == 0) { + ret = 0; + goto key_search_all_out; + } + + /* list the keys in the keyring */ + pk = keylist; + do { + key = *pk++; + + ret = keyctl_describe_alloc(key, &buffer); + if (ret < 0) + continue; + + n = sscanf(buffer, "%*[^;];%*d;%*d;%*x;%n", &dpos); + if (n) { + free(buffer); + continue; + } + + if (strstr(buffer + dpos, THIS_PROGRAM_NAME ":") == + buffer + dpos + ) { + ret = key; + free(buffer); + goto key_search_all_out; + } + free(buffer); + + } while (--count); + + ret = 0; + +key_search_all_out: + free(keylist); + return ret; +} + +/* add or update a specific key to keyring */ +static key_serial_t +key_add(const char *addr, const char *user, + const char *domain, const char *pass) +{ + char desc[INET6_ADDRSTRLEN + MAX_USERNAME_SIZE + MAX_DOMAIN_SIZE + \ + + sizeof(THIS_PROGRAM_NAME) + 3]; + + create_description(addr, user, domain, desc); + + return add_key("user", desc, pass, strnlen(pass, MOUNT_PASSWD_SIZE) + 1, + DEST_KEYRING); +} + +/* add command handler */ +static int cifscreds_add(int argc, char *argv[]) +{ + char addrstr[MAX_ADDR_LIST_LEN]; + char *currentaddress, *nextaddress; + char *pass; + int ret; + + if (argc != 4 && argc != 5) + usage(); + + ret = resolve_host(argv[2], addrstr); + switch (ret) { + case EX_USAGE: + fprintf(stderr, "error: Could not resolve address " + "for %s\n", argv[2]); + return EXIT_FAILURE; + + case EX_SYSERR: + fprintf(stderr, "error: Problem parsing address list\n"); + return EXIT_FAILURE; + } + + if (strpbrk(argv[3], USER_DISALLOWED_CHARS)) { + fprintf(stderr, "error: Incorrect username\n"); + return EXIT_FAILURE; + } + + if (argc == 5) { + if (strspn(argv[4], DOMAIN_ALLOWED_CHARS) != + strnlen(argv[4], MAX_DOMAIN_SIZE) + ) { + fprintf(stderr, "error: Incorrect domain name\n"); + return EXIT_FAILURE; + } + } + + /* search for same credentials stashed for current host */ + currentaddress = addrstr; + nextaddress = strchr(currentaddress, ','); + if (nextaddress) + *nextaddress++ = '\0'; + + while (currentaddress) { + if (key_search(currentaddress, argv[3], + argc == 5 ? argv[4] : NULL) > 0 + ) { + printf("You already have stashed credentials " + "for %s (%s)\n", currentaddress, argv[2]); + printf("If you want to update them use:\n"); + printf("\t%s update\n", thisprogram); + + return EXIT_FAILURE; + } + + currentaddress = nextaddress; + if (currentaddress) { + *(currentaddress - 1) = ','; + nextaddress = strchr(currentaddress, ','); + if (nextaddress) + *nextaddress++ = '\0'; + } + } + + /* + * if there isn't same credentials stashed add them to keyring + * and set permisson mask + */ + pass = getpass("Password: "); + + currentaddress = addrstr; + nextaddress = strchr(currentaddress, ','); + if (nextaddress) + *nextaddress++ = '\0'; + + while (currentaddress) { + key_serial_t key = key_add(currentaddress, argv[3], + argc == 5 ? argv[4] : NULL, pass); + if (key <= 0) { + fprintf(stderr, "error: Add credential key for %s\n", + currentaddress); + } else { + if (keyctl(KEYCTL_SETPERM, key, KEY_POS_VIEW | \ + KEY_POS_WRITE | KEY_USR_VIEW | \ + KEY_USR_WRITE) < 0 + ) { + fprintf(stderr, "error: Setting permissons " + "on key, attempt to delete...\n"); + + if (keyctl(KEYCTL_UNLINK, key, DEST_KEYRING) < 0) { + fprintf(stderr, "error: Deleting key from " + "keyring for %s (%s)\n", + currentaddress, argv[2]); + } + } + } + + currentaddress = nextaddress; + if (currentaddress) { + nextaddress = strchr(currentaddress, ','); + if (nextaddress) + *nextaddress++ = '\0'; + } + } + + return EXIT_SUCCESS; +} + +/* clear command handler */ +static int cifscreds_clear(int argc, char *argv[]) +{ + char addrstr[MAX_ADDR_LIST_LEN]; + char *currentaddress, *nextaddress; + int ret, count = 0, errors = 0; + + if (argc != 4 && argc != 5) + usage(); + + ret = resolve_host(argv[2], addrstr); + switch (ret) { + case EX_USAGE: + fprintf(stderr, "error: Could not resolve address " + "for %s\n", argv[2]); + return EXIT_FAILURE; + + case EX_SYSERR: + fprintf(stderr, "error: Problem parsing address list\n"); + return EXIT_FAILURE; + } + + if (strpbrk(argv[3], USER_DISALLOWED_CHARS)) { + fprintf(stderr, "error: Incorrect username\n"); + return EXIT_FAILURE; + } + + if (argc == 5) { + if (strspn(argv[4], DOMAIN_ALLOWED_CHARS) != + strnlen(argv[4], MAX_DOMAIN_SIZE) + ) { + fprintf(stderr, "error: Incorrect domain name\n"); + return EXIT_FAILURE; + } + } + + /* + * search for same credentials stashed for current host + * and unlink them from session keyring + */ + currentaddress = addrstr; + nextaddress = strchr(currentaddress, ','); + if (nextaddress) + *nextaddress++ = '\0'; + + while (currentaddress) { + key_serial_t key = key_search(currentaddress, argv[3], + argc == 5 ? argv[4] : NULL); + if (key > 0) { + if (keyctl(KEYCTL_UNLINK, key, DEST_KEYRING) < 0) { + fprintf(stderr, "error: Removing key from " + "keyring for %s (%s)\n", + currentaddress, argv[2]); + errors++; + } else { + count++; + } + } + + currentaddress = nextaddress; + if (currentaddress) { + nextaddress = strchr(currentaddress, ','); + if (nextaddress) + *nextaddress++ = '\0'; + } + } + + if (!count && !errors) { + printf("You have no same stashed credentials " + " for %s\n", argv[2]); + printf("If you want to add them use:\n"); + printf("\t%s add\n", thisprogram); + + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +/* clearall command handler */ +static int cifscreds_clearall(int argc, char *argv[]) +{ + key_serial_t key; + int count = 0, errors = 0; + + if (argc != 2) + usage(); + + /* + * search for all program's credentials stashed in session keyring + * and then unlink them + */ + do { + key = key_search_all(); + if (key > 0) { + if (keyctl(KEYCTL_UNLINK, key, DEST_KEYRING) < 0) { + fprintf(stderr, "error: Deleting key " + "from keyring"); + errors++; + } else { + count++; + } + } + } while (key > 0); + + if (!count && !errors) { + printf("You have no stashed " THIS_PROGRAM_NAME + " credentials\n"); + printf("If you want to add them use:\n"); + printf("\t%s add\n", thisprogram); + + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +/* update command handler */ +static int cifscreds_update(int argc, char *argv[]) +{ + char addrstr[MAX_ADDR_LIST_LEN]; + char *currentaddress, *nextaddress, *pass; + char *addrs[16]; + int ret, id, count = 0; + + if (argc != 4 && argc != 5) + usage(); + + ret = resolve_host(argv[2], addrstr); + switch (ret) { + case EX_USAGE: + fprintf(stderr, "error: Could not resolve address " + "for %s\n", argv[2]); + return EXIT_FAILURE; + + case EX_SYSERR: + fprintf(stderr, "error: Problem parsing address list\n"); + return EXIT_FAILURE; + } + + if (strpbrk(argv[3], USER_DISALLOWED_CHARS)) { + fprintf(stderr, "error: Incorrect username\n"); + return EXIT_FAILURE; + } + + if (argc == 5) { + if (strspn(argv[4], DOMAIN_ALLOWED_CHARS) != + strnlen(argv[4], MAX_DOMAIN_SIZE) + ) { + fprintf(stderr, "error: Incorrect domain name\n"); + return EXIT_FAILURE; + } + } + + /* search for necessary credentials stashed in session keyring */ + currentaddress = addrstr; + nextaddress = strchr(currentaddress, ','); + if (nextaddress) + *nextaddress++ = '\0'; + + while (currentaddress) { + if (key_search(currentaddress, argv[3], + argc == 5 ? argv[4] : NULL) > 0 + ) { + addrs[count] = currentaddress; + count++; + } + + currentaddress = nextaddress; + if (currentaddress) { + nextaddress = strchr(currentaddress, ','); + if (nextaddress) + *nextaddress++ = '\0'; + } + } + + if (!count) { + printf("You have no same stashed credentials " + "for %s\n", argv[2]); + printf("If you want to add them use:\n"); + printf("\t%s add\n", thisprogram); + + return EXIT_FAILURE; + } + + /* update payload of found keys */ + pass = getpass("Password: "); + + for (id = 0; id < count; id++) { + key_serial_t key = key_add(addrs[id], argv[3], + argc == 5 ? argv[4] : NULL, pass); + if (key <= 0) + fprintf(stderr, "error: Update credential key " + "for %s\n", addrs[id]); + } + + return EXIT_SUCCESS; +} + +int main(int argc, char **argv) +{ + struct command *cmd, *best; + int n; + + thisprogram = (char *)basename(argv[0]); + if (thisprogram == NULL) + thisprogram = THIS_PROGRAM_NAME; + + if (argc == 1) + usage(); + + /* find the best fit command */ + best = NULL; + n = strnlen(argv[1], MAX_COMMAND_SIZE); + + for (cmd = commands; cmd->action; cmd++) { + if (memcmp(cmd->name, argv[1], n) != 0) + continue; + + if (cmd->name[n] == 0) { + /* exact match */ + best = cmd; + break; + } + + /* partial match */ + if (best) { + fprintf(stderr, "Ambiguous command\n"); + exit(EXIT_FAILURE); + } + + best = cmd; + } + + if (!best) { + fprintf(stderr, "Unknown command\n"); + exit(EXIT_FAILURE); + } + + exit(best->action(argc, argv)); +} diff --git a/configure.ac b/configure.ac index 266380a..c7d420d 100644 --- a/configure.ac +++ b/configure.ac @@ -16,12 +16,18 @@ AC_ARG_ENABLE(cifsupcall, enable_cifsupcall=$enableval, enable_cifsupcall="maybe") +AC_ARG_ENABLE(cifscreds, + [AC_HELP_STRING([--enable-cifscreds], + [Create cifscreds utility @<:@default=no@:>@])], + enable_cifscreds=$enableval, + enable_cifscreds="no") + # Checks for programs. AC_PROG_CC AC_GNU_SOURCE # Checks for header files. -AC_CHECK_HEADERS([arpa/inet.h fcntl.h inttypes.h limits.h mntent.h netdb.h stddef.h stdint.h stdlib.h string.h strings.h sys/mount.h sys/param.h sys/socket.h sys/time.h syslog.h unistd.h], , [AC_MSG_ERROR([necessary header(s) not found])]) +AC_CHECK_HEADERS([arpa/inet.h ctype.h fcntl.h inttypes.h limits.h mntent.h netdb.h stddef.h stdint.h stdlib.h string.h strings.h sys/mount.h sys/param.h sys/socket.h sys/time.h syslog.h unistd.h], , [AC_MSG_ERROR([necessary header(s) not found])]) if test $enable_cifsupcall != "no"; then AC_CHECK_HEADERS([krb5.h krb5/krb5.h]) @@ -82,6 +88,10 @@ if test $enable_cifsupcall != "no"; then AC_SUBST(KRB5_LDADD) fi +if test $enable_cifscreds = "yes"; then + AC_CHECK_HEADERS([keyutils.h], , [AC_MSG_ERROR([keyutils.h not found, consider installing keyutils-libs-devel.])]) +fi + # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL AC_TYPE_UID_T @@ -98,7 +108,7 @@ AC_FUNC_REALLOC AC_FUNC_STRNLEN # check for required functions -AC_CHECK_FUNCS([alarm atexit endpwent getmntent getpass gettimeofday inet_ntop memset realpath setenv strchr strdup strerror strncasecmp strndup strpbrk strrchr strstr strtol strtoul uname], , [AC_MSG_ERROR([necessary functions(s) not found])]) +AC_CHECK_FUNCS([alarm atexit endpwent getmntent getpass gettimeofday inet_ntop memset realpath setenv strchr strcmp strdup strerror strncasecmp strndup strpbrk strrchr strstr strtol strtoul tolower uname], , [AC_MSG_ERROR([necessary functions(s) not found])]) # ugly, but I'm not sure how to check for functions in a library that's not in $LIBS cu_saved_libs=$LIBS @@ -117,6 +127,7 @@ fi LIBS=$cu_saved_libs AM_CONDITIONAL(CONFIG_CIFSUPCALL, [test "$enable_cifsupcall" != "no"]) +AM_CONDITIONAL(CONFIG_CIFSCREDS, [test "$enable_cifscreds" = "yes"]) LIBCAP_NG_PATH diff --git a/mount.cifs.c b/mount.cifs.c index 3623e76..ed27bba 100644 --- a/mount.cifs.c +++ b/mount.cifs.c @@ -56,6 +56,7 @@ #endif /* HAVE_LIBCAP_NG */ #include "mount.h" #include "util.h" +#include "resolve_host.h" #ifndef MS_MOVE #define MS_MOVE 8192 @@ -87,12 +88,6 @@ /* max length of username (somewhat made up here) */ #define MAX_USERNAME_SIZE 32 -/* currently maximum length of IPv6 address string */ -#define MAX_ADDRESS_LEN INET6_ADDRSTRLEN - -/* limit list of addresses to 16 max-size addrs */ -#define MAX_ADDR_LIST_LEN ((MAX_ADDRESS_LEN + 1) * 16) - #ifndef SAFE_FREE #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x = NULL; } } while (0) #endif @@ -1207,90 +1202,6 @@ nocopy: return 0; } -/* - * resolve "host" portion of parsed info to comma-separated list of - * address(es) - */ -static int resolve_host(struct parsed_mount_info *parsed_info) -{ - int rc; - /* 10 for max width of decimal scopeid */ - char tmpbuf[NI_MAXHOST + 1 + 10 + 1]; - const char *ipaddr; - size_t len; - struct addrinfo *addrlist, *addr; - struct sockaddr_in *sin; - struct sockaddr_in6 *sin6; - - rc = getaddrinfo(parsed_info->host, NULL, NULL, &addrlist); - if (rc != 0) { - fprintf(stderr, "mount error: could not resolve address for " - "%s: %s\n", parsed_info->host, - rc == EAI_SYSTEM ? strerror(errno) : gai_strerror(rc)); - /* FIXME: return better error based on rc? */ - return EX_USAGE; - } - - addr = addrlist; - while (addr) { - /* skip non-TCP entries */ - if (addr->ai_socktype != SOCK_STREAM || - addr->ai_protocol != IPPROTO_TCP) { - addr = addr->ai_next; - continue; - } - - switch (addr->ai_addr->sa_family) { - case AF_INET6: - sin6 = (struct sockaddr_in6 *)addr->ai_addr; - ipaddr = inet_ntop(AF_INET6, &sin6->sin6_addr, tmpbuf, - sizeof(tmpbuf)); - if (!ipaddr) { - rc = EX_SYSERR; - fprintf(stderr, - "mount error: problem parsing address " - "list: %s\n", strerror(errno)); - goto resolve_host_out; - } - - if (sin6->sin6_scope_id) { - len = strnlen(tmpbuf, sizeof(tmpbuf)); - ipaddr = tmpbuf + len; - snprintf(tmpbuf, sizeof(tmpbuf) - len, "%%%u", - sin6->sin6_scope_id); - } - break; - case AF_INET: - sin = (struct sockaddr_in *)addr->ai_addr; - ipaddr = inet_ntop(AF_INET, &sin->sin_addr, tmpbuf, - sizeof(tmpbuf)); - if (!ipaddr) { - rc = EX_SYSERR; - fprintf(stderr, - "mount error: problem parsing address " - "list: %s\n", strerror(errno)); - goto resolve_host_out; - } - - break; - default: - addr = addr->ai_next; - continue; - } - - if (parsed_info->addrlist[0] != '\0') - strlcat(parsed_info->addrlist, ",", - sizeof(parsed_info->addrlist)); - strlcat(parsed_info->addrlist, tmpbuf, - sizeof(parsed_info->addrlist)); - addr = addr->ai_next; - } - -resolve_host_out: - freeaddrinfo(addrlist); - return rc; -} - static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info) { int length = strnlen(unc_name, MAX_UNC_LEN); @@ -1645,10 +1556,20 @@ assemble_mountinfo(struct parsed_mount_info *parsed_info, if (rc) goto assemble_exit; - rc = resolve_host(parsed_info); - if (rc) + rc = resolve_host(parsed_info->host, parsed_info->addrlist); + switch (rc) { + case EX_USAGE: + fprintf(stderr, "mount error: could not resolve address for " + "%s: %s\n", parsed_info->host, + rc == EAI_SYSTEM ? strerror(errno) : gai_strerror(rc)); goto assemble_exit; + case EX_SYSERR: + fprintf(stderr, "mount error: problem parsing address " + "list: %s\n", strerror(errno)); + goto assemble_exit; + } + if (!parsed_info->got_user) { /* * Note that the password will not be retrieved from the diff --git a/resolve_host.c b/resolve_host.c new file mode 100644 index 0000000..02b8096 --- /dev/null +++ b/resolve_host.c @@ -0,0 +1,106 @@ +/* + * resolving DNS hostname routine + * + * Copyright (C) 2010 Jeff Layton (jlayton@samba.org) + * Copyright (C) 2010 Igor Druzhinin (jaxbrigs@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include + +#include "mount.h" +#include "util.h" +#include "resolve_host.h" + +/* + * resolve hostname to comma-separated list of address(es) + */ +int resolve_host(const char *host, char *addrstr) +{ + int rc; + /* 10 for max width of decimal scopeid */ + char tmpbuf[NI_MAXHOST + 1 + 10 + 1]; + const char *ipaddr; + size_t len; + struct addrinfo *addrlist, *addr; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + rc = getaddrinfo(host, NULL, NULL, &addrlist); + if (rc != 0) + return EX_USAGE; + + addr = addrlist; + while (addr) { + /* skip non-TCP entries */ + if (addr->ai_socktype != SOCK_STREAM || + addr->ai_protocol != IPPROTO_TCP) { + addr = addr->ai_next; + continue; + } + + switch (addr->ai_addr->sa_family) { + case AF_INET6: + sin6 = (struct sockaddr_in6 *)addr->ai_addr; + ipaddr = inet_ntop(AF_INET6, &sin6->sin6_addr, tmpbuf, + sizeof(tmpbuf)); + if (!ipaddr) { + rc = EX_SYSERR; + goto resolve_host_out; + } + + if (sin6->sin6_scope_id) { + len = strnlen(tmpbuf, sizeof(tmpbuf)); + ipaddr = tmpbuf + len; + snprintf(tmpbuf, sizeof(tmpbuf) - len, "%%%u", + sin6->sin6_scope_id); + } + break; + case AF_INET: + sin = (struct sockaddr_in *)addr->ai_addr; + ipaddr = inet_ntop(AF_INET, &sin->sin_addr, tmpbuf, + sizeof(tmpbuf)); + if (!ipaddr) { + rc = EX_SYSERR; + goto resolve_host_out; + } + + break; + default: + addr = addr->ai_next; + continue; + } + + if (addr == addrlist) + *addrstr = '\0'; + else + strlcat(addrstr, ",", MAX_ADDR_LIST_LEN); + + strlcat(addrstr, tmpbuf, MAX_ADDR_LIST_LEN); + addr = addr->ai_next; + } + +resolve_host_out: + freeaddrinfo(addrlist); + return rc; +} diff --git a/resolve_host.h b/resolve_host.h new file mode 100644 index 0000000..b949245 --- /dev/null +++ b/resolve_host.h @@ -0,0 +1,34 @@ +/* + * resolving DNS hostname routine + * + * Copyright (C) 2010 Jeff Layton (jlayton@samba.org) + * Copyright (C) 2010 Igor Druzhinin (jaxbrigs@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _RESOLVE_HOST_H_ +#define _RESOLVE_HOST_H_ + +#include + +/* currently maximum length of IPv6 address string */ +#define MAX_ADDRESS_LEN INET6_ADDRSTRLEN + +/* limit list of addresses to 16 max-size addrs */ +#define MAX_ADDR_LIST_LEN ((MAX_ADDRESS_LEN + 1) * 16) + +extern int resolve_host(const char *host, char *addrstr); + +#endif /* _RESOLVE_HOST_H_ */