]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - utils/gssd/gssd_proc.c
nfs-utils: store the address given in the upcall for later use
[nfs-utils.git] / utils / gssd / gssd_proc.c
index a145081e8ae234627ad2b98c1db78fbd8b25061d..6a68cdf6d1744fb19f57bc4a0b22ee82d2c1b5f0 100644 (file)
 
 */
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
 #ifndef _GNU_SOURCE
 #define _GNU_SOURCE
 #endif
-#include "config.h"
+
 #include <sys/param.h>
 #include <rpc/rpc.h>
 #include <sys/stat.h>
@@ -66,7 +70,6 @@
 #include "gssd.h"
 #include "err_util.h"
 #include "gss_util.h"
-#include "gss_oids.h"
 #include "krb5_util.h"
 #include "context.h"
 
@@ -99,12 +102,68 @@ struct pollfd * pollarray;
 
 int pollsize;  /* the size of pollaray (in pollfd's) */
 
+/*
+ * convert a presentation address string to a sockaddr_storage struct. Returns
+ * true on success and false on failure.
+ */
+static int
+addrstr_to_sockaddr(struct sockaddr *sa, const char *addr, const int port)
+{
+       struct sockaddr_in      *s4 = (struct sockaddr_in *) sa;
+
+       if (inet_pton(AF_INET, addr, &s4->sin_addr)) {
+               s4->sin_family = AF_INET;
+               s4->sin_port = htons(port);
+       } else {
+               printerr(0, "ERROR: unable to convert %s to address\n", addr);
+               return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * convert a sockaddr to a hostname
+ */
+static char *
+sockaddr_to_hostname(const struct sockaddr *sa, const char *addr)
+{
+       socklen_t               addrlen;
+       int                     err;
+       char                    *hostname;
+       char                    hbuf[NI_MAXHOST];
+
+       switch (sa->sa_family) {
+       case AF_INET:
+               addrlen = sizeof(struct sockaddr_in);
+               break;
+       default:
+               printerr(0, "ERROR: unrecognized addr family %d\n",
+                        sa->sa_family);
+               return NULL;
+       }
+
+       err = getnameinfo(sa, addrlen, hbuf, sizeof(hbuf), NULL, 0,
+                         NI_NAMEREQD);
+       if (err) {
+               printerr(0, "ERROR: unable to resolve %s to hostname: %s\n",
+                        addr, err == EAI_SYSTEM ? strerror(err) :
+                                                  gai_strerror(err));
+               return NULL;
+       }
+
+       hostname = strdup(hbuf);
+
+       return hostname;
+}
+
 /* XXX buffer problems: */
 static int
 read_service_info(char *info_file_name, char **servicename, char **servername,
-                 int *prog, int *vers, char **protocol, int *port) {
+                 int *prog, int *vers, char **protocol,
+                 struct sockaddr *addr) {
 #define INFOBUFLEN 256
-       char            buf[INFOBUFLEN];
+       char            buf[INFOBUFLEN + 1];
        static char     dummy[128];
        int             nbytes;
        static char     service[128];
@@ -114,10 +173,9 @@ read_service_info(char *info_file_name, char **servicename, char **servername,
        char            protoname[16];
        char            cb_port[128];
        char            *p;
-       in_addr_t       inaddr;
        int             fd = -1;
-       struct hostent  *ent = NULL;
        int             numfields;
+       int             port = 0;
 
        *servicename = *servername = *protocol = NULL;
 
@@ -129,6 +187,7 @@ read_service_info(char *info_file_name, char **servicename, char **servername,
        if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1)
                goto fail;
        close(fd);
+       buf[nbytes] = '\0';
 
        numfields = sscanf(buf,"RPC server: %127s\n"
                   "service: %127s %15s version %15s\n"
@@ -156,21 +215,26 @@ read_service_info(char *info_file_name, char **servicename, char **servername,
        if((*prog != 100003) || ((*vers != 2) && (*vers != 3) && (*vers != 4)))
                goto fail;
 
-       /* create service name */
-       inaddr = inet_addr(address);
-       if (!(ent = gethostbyaddr(&inaddr, sizeof(inaddr), AF_INET))) {
-               printerr(0, "ERROR: can't resolve server %s name\n", address);
-               goto fail;
+       if (cb_port[0] != '\0') {
+               port = atoi(cb_port);
+               if (port < 0 || port > 65535)
+                       goto fail;
        }
-       if (!(*servername = calloc(strlen(ent->h_name) + 1, 1)))
+
+       if (!addrstr_to_sockaddr(addr, address, port))
+               goto fail;
+
+       *servername = sockaddr_to_hostname(addr, address);
+       if (*servername == NULL)
                goto fail;
-       memcpy(*servername, ent->h_name, strlen(ent->h_name));
-       snprintf(buf, INFOBUFLEN, "%s@%s", service, ent->h_name);
+
+       nbytes = snprintf(buf, INFOBUFLEN, "%s@%s", service, *servername);
+       if (nbytes > INFOBUFLEN)
+               goto fail;
+
        if (!(*servicename = calloc(strlen(buf) + 1, 1)))
                goto fail;
        memcpy(*servicename, buf, strlen(buf));
-       if (cb_port[0] != '\0')
-               *port = atoi(cb_port);
 
        if (!(*protocol = strdup(protoname)))
                goto fail;
@@ -178,9 +242,10 @@ read_service_info(char *info_file_name, char **servicename, char **servername,
 fail:
        printerr(0, "ERROR: failed to read service info\n");
        if (fd != -1) close(fd);
-       if (*servername) free(*servername);
-       if (*servicename) free(*servicename);
-       if (*protocol) free(*protocol);
+       free(*servername);
+       free(*servicename);
+       free(*protocol);
+       *servicename = *servername = *protocol = NULL;
        return -1;
 }
 
@@ -196,10 +261,10 @@ destroy_client(struct clnt_info *clp)
        if (clp->dir_fd != -1) close(clp->dir_fd);
        if (clp->krb5_fd != -1) close(clp->krb5_fd);
        if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
-       if (clp->dirname) free(clp->dirname);
-       if (clp->servicename) free(clp->servicename);
-       if (clp->servername) free(clp->servername);
-       if (clp->protocol) free(clp->protocol);
+       free(clp->dirname);
+       free(clp->servicename);
+       free(clp->servername);
+       free(clp->protocol);
        free(clp);
 }
 
@@ -246,7 +311,7 @@ process_clnt_dir_files(struct clnt_info * clp)
        if ((clp->servicename == NULL) &&
             read_service_info(info_file_name, &clp->servicename,
                                &clp->servername, &clp->prog, &clp->vers,
-                               &clp->protocol, &clp->port))
+                               &clp->protocol, (struct sockaddr *) &clp->addr))
                return -1;
        return 0;
 }
@@ -427,7 +492,7 @@ do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
            gss_buffer_desc *context_token)
 {
        char    *buf = NULL, *p = NULL, *end = NULL;
-       unsigned int timeout = 0; /* XXX decide on a reasonable value */
+       unsigned int timeout = context_timeout;
        unsigned int buf_size = 0;
 
        printerr(1, "doing downcall\n");
@@ -438,7 +503,6 @@ do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
        end = buf + buf_size;
 
        if (WRITE_BYTES(&p, end, uid)) goto out_err;
-       /* Not setting any timeout for now: */
        if (WRITE_BYTES(&p, end, timeout)) goto out_err;
        if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err;
        if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err;
@@ -449,7 +513,7 @@ do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
        return 0;
 out_err:
        if (buf) free(buf);
-       printerr(0, "Failed to write downcall!\n");
+       printerr(1, "Failed to write downcall!\n");
        return -1;
 }
 
@@ -499,6 +563,7 @@ int create_auth_rpc_client(struct clnt_info *clp,
        struct addrinfo         ai_hints, *a = NULL;
        char                    service[64];
        char                    *at_sign;
+       struct sockaddr_in      *addr = (struct sockaddr_in *) &clp->addr;
 
        /* Create the context as the user (not as root) */
        save_uid = geteuid();
@@ -595,8 +660,10 @@ int create_auth_rpc_client(struct clnt_info *clp,
                         clp->servername, uid);
                goto out_fail;
        }
-       if (clp->port)
-               ((struct sockaddr_in *)a->ai_addr)->sin_port = htons(clp->port);
+
+       if (addr->sin_port != 0)
+               ((struct sockaddr_in *) a->ai_addr)->sin_port = addr->sin_port;
+
        if (a->ai_protocol == IPPROTO_TCP) {
                if ((rpc_clnt = clnttcp_create(
                                        (struct sockaddr_in *) a->ai_addr,
@@ -742,14 +809,14 @@ handle_krb5_upcall(struct clnt_info *clp)
                        }
                        gssd_free_krb5_machine_cred_list(credlist);
                        if (!success) {
-                               printerr(0, "WARNING: Failed to create krb5 context "
+                               printerr(1, "WARNING: Failed to create krb5 context "
                                         "for user with uid %d with any "
                                         "credentials cache for server %s\n",
                                         uid, clp->servername);
                                goto out_return_error;
                        }
                } else {
-                       printerr(0, "WARNING: Failed to create krb5 context "
+                       printerr(1, "WARNING: Failed to create krb5 context "
                                 "for user with uid %d for server %s\n",
                                 uid, clp->servername);
                        goto out_return_error;
@@ -757,13 +824,13 @@ handle_krb5_upcall(struct clnt_info *clp)
        }
 
        if (!authgss_get_private_data(auth, &pd)) {
-               printerr(0, "WARNING: Failed to obtain authentication "
+               printerr(1, "WARNING: Failed to obtain authentication "
                            "data for user with uid %d for server %s\n",
                         uid, clp->servername);
                goto out_return_error;
        }
 
-       if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid)) {
+       if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid, NULL)) {
                printerr(0, "WARNING: Failed to serialize krb5 context for "
                            "user with uid %d for server %s\n",
                         uid, clp->servername);
@@ -775,8 +842,10 @@ handle_krb5_upcall(struct clnt_info *clp)
 out:
        if (token.value)
                free(token.value);
+#ifndef HAVE_LIBTIRPC
        if (pd.pd_ctx_hndl.length != 0)
                authgss_free_private_data(&pd);
+#endif
        if (auth)
                AUTH_DESTROY(auth);
        if (rpc_clnt)
@@ -825,7 +894,7 @@ handle_spkm3_upcall(struct clnt_info *clp)
                goto out_return_error;
        }
 
-       if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid)) {
+       if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid, NULL)) {
                printerr(0, "WARNING: Failed to serialize spkm3 context for "
                            "user with uid %d for server\n",
                         uid, clp->servername);