]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - utils/gssd/gssd_proc.c
nfsidmap: Added Error Logging
[nfs-utils.git] / utils / gssd / gssd_proc.c
index 33a31c3c3c5b1c018ea8a7408bba34863569053b..41328c9b18483e8f7f5d9a2edc8eb9bce9465e30 100644 (file)
@@ -108,7 +108,7 @@ 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.
+ * true on success or false on failure.
  *
  * Note that we do not populate the sin6_scope_id field here for IPv6 addrs.
  * gssd nececessarily relies on hostname resolution and DNS AAAA records
@@ -120,26 +120,43 @@ int pollsize;  /* the size of pollaray (in pollfd's) */
  * not really feasible at present.
  */
 static int
-addrstr_to_sockaddr(struct sockaddr *sa, const char *addr, const int port)
+addrstr_to_sockaddr(struct sockaddr *sa, const char *node, const char *port)
 {
-       struct sockaddr_in      *s4 = (struct sockaddr_in *) sa;
-#ifdef IPV6_SUPPORTED
-       struct sockaddr_in6     *s6 = (struct sockaddr_in6 *) sa;
-#endif /* IPV6_SUPPORTED */
+       int rc;
+       struct addrinfo *res;
+       struct addrinfo hints = { .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV };
 
-       if (inet_pton(AF_INET, addr, &s4->sin_addr)) {
-               s4->sin_family = AF_INET;
-               s4->sin_port = htons(port);
-#ifdef IPV6_SUPPORTED
-       } else if (inet_pton(AF_INET6, addr, &s6->sin6_addr)) {
-               s6->sin6_family = AF_INET6;
-               s6->sin6_port = htons(port);
+#ifndef IPV6_SUPPORTED
+       hints.ai_family = AF_INET;
 #endif /* IPV6_SUPPORTED */
-       } else {
-               printerr(0, "ERROR: unable to convert %s to address\n", addr);
+
+       rc = getaddrinfo(node, port, &hints, &res);
+       if (rc) {
+               printerr(0, "ERROR: unable to convert %s|%s to sockaddr: %s\n",
+                        node, port, rc == EAI_SYSTEM ? strerror(errno) :
+                                               gai_strerror(rc));
                return 0;
        }
 
+#ifdef IPV6_SUPPORTED
+       /*
+        * getnameinfo ignores the scopeid. If the address turns out to have
+        * a non-zero scopeid, we can't use it -- the resolved host might be
+        * completely different from the one intended.
+        */
+       if (res->ai_addr->sa_family == AF_INET6) {
+               struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)res->ai_addr;
+               if (sin6->sin6_scope_id) {
+                       printerr(0, "ERROR: address %s has non-zero "
+                                   "sin6_scope_id!\n", node);
+                       freeaddrinfo(res);
+                       return 0;
+               }
+       }
+#endif /* IPV6_SUPPORTED */
+
+       memcpy(sa, res->ai_addr, res->ai_addrlen);
+       freeaddrinfo(res);
        return 1;
 }
 
@@ -197,11 +214,10 @@ read_service_info(char *info_file_name, char **servicename, char **servername,
        char            program[16];
        char            version[16];
        char            protoname[16];
-       char            cb_port[128];
+       char            port[128];
        char            *p;
        int             fd = -1;
        int             numfields;
-       int             port = 0;
 
        *servicename = *servername = *protocol = NULL;
 
@@ -230,9 +246,9 @@ read_service_info(char *info_file_name, char **servicename, char **servername,
                goto fail;
        }
 
-       cb_port[0] = '\0';
+       port[0] = '\0';
        if ((p = strstr(buf, "port")) != NULL)
-               sscanf(p, "port: %127s\n", cb_port);
+               sscanf(p, "port: %127s\n", port);
 
        /* check service, program, and version */
        if (memcmp(service, "nfs", 3) != 0)
@@ -249,12 +265,6 @@ read_service_info(char *info_file_name, char **servicename, char **servername,
                        goto fail;
        }
 
-       if (cb_port[0] != '\0') {
-               port = atoi(cb_port);
-               if (port < 0 || port > 65535)
-                       goto fail;
-       }
-
        if (!addrstr_to_sockaddr(addr, address, port))
                goto fail;
 
@@ -590,6 +600,67 @@ update_client_list(void)
        return retval;
 }
 
+/* Encryption types supported by the kernel rpcsec_gss code */
+int num_krb5_enctypes = 0;
+krb5_enctype *krb5_enctypes = NULL;
+
+/*
+ * Parse the supported encryption type information
+ */
+static int
+parse_enctypes(char *enctypes)
+{
+       int n = 0;
+       char *curr, *comma;
+       int i;
+       static char *cached_types;
+
+       if (cached_types && strcmp(cached_types, enctypes) == 0)
+               return 0;
+       free(cached_types);
+
+       if (krb5_enctypes != NULL) {
+               free(krb5_enctypes);
+               krb5_enctypes = NULL;
+               num_krb5_enctypes = 0;
+       }
+
+       /* count the number of commas */
+       for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) {
+               comma = strchr(curr, ',');
+               if (comma != NULL)
+                       n++;
+               else
+                       break;
+       }
+       /* If no more commas and we're not at the end, there's one more value */
+       if (*curr != '\0')
+               n++;
+
+       /* Empty string, return an error */
+       if (n == 0)
+               return ENOENT;
+
+       /* Allocate space for enctypes array */
+       if ((krb5_enctypes = (int *) calloc(n, sizeof(int))) == NULL) {
+               return ENOMEM;
+       }
+
+       /* Now parse each value into the array */
+       for (curr = enctypes, i = 0; curr && *curr != '\0'; curr = ++comma) {
+               krb5_enctypes[i++] = atoi(curr);
+               comma = strchr(curr, ',');
+               if (comma == NULL)
+                       break;
+       }
+
+       num_krb5_enctypes = n;
+       if ((cached_types = malloc(strlen(enctypes)+1)))
+               strcpy(cached_types, enctypes);
+
+       return 0;
+}
+
 static int
 do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
            gss_buffer_desc *context_token)
@@ -788,7 +859,7 @@ int create_auth_rpc_client(struct clnt_info *clp,
                 * Do this before creating rpc connection since we won't need
                 * rpc connection if it fails!
                 */
-               if (limit_krb5_enctypes(&sec, uid)) {
+               if (limit_krb5_enctypes(&sec)) {
                        printerr(1, "WARNING: Failed while limiting krb5 "
                                    "encryption types for user with uid %d\n",
                                 uid);
@@ -865,7 +936,7 @@ int create_auth_rpc_client(struct clnt_info *clp,
        if (sec.cred != GSS_C_NO_CREDENTIAL)
                gss_release_cred(&min_stat, &sec.cred);
        /* Restore euid to original value */
-       if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
+       if (((int)save_uid != -1) && (setfsuid(save_uid) != (int)uid)) {
                printerr(0, "WARNING: Failed to restore fsuid"
                            " to uid %d from %d\n", save_uid, uid);
        }
@@ -894,6 +965,7 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
        char                    **ccname;
        char                    **dirname;
        int                     create_resp = -1;
+       int                     err, downcall_err = -EACCES;
 
        printerr(1, "handling krb5 upcall (%s)\n", clp->dirname);
 
@@ -934,7 +1006,10 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
                                service == NULL)) {
                /* Tell krb5 gss which credentials cache to use */
                for (dirname = ccachesearch; *dirname != NULL; dirname++) {
-                       if (gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname) == 0)
+                       err = gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname);
+                       if (err == -EKEYEXPIRED)
+                               downcall_err = -EKEYEXPIRED;
+                       else if (!err)
                                create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
                                                             AUTHTYPE_KRB5);
                        if (create_resp == 0)
@@ -1024,7 +1099,7 @@ out:
        return;
 
 out_return_error:
-       do_error_downcall(fd, uid, -1);
+       do_error_downcall(fd, uid, downcall_err);
        goto out;
 }
 
@@ -1086,7 +1161,7 @@ handle_krb5_upcall(struct clnt_info *clp)
 {
        uid_t                   uid;
 
-       if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
+       if (read(clp->krb5_fd, &uid, sizeof(uid)) < (ssize_t)sizeof(uid)) {
                printerr(0, "WARNING: failed reading uid from krb5 "
                            "upcall pipe: %s\n", strerror(errno));
                return;
@@ -1100,7 +1175,7 @@ handle_spkm3_upcall(struct clnt_info *clp)
 {
        uid_t                   uid;
 
-       if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
+       if (read(clp->spkm3_fd, &uid, sizeof(uid)) < (ssize_t)sizeof(uid)) {
                printerr(0, "WARNING: failed reading uid from spkm3 "
                         "upcall pipe: %s\n", strerror(errno));
                return;
@@ -1119,6 +1194,7 @@ handle_gssd_upcall(struct clnt_info *clp)
        char                    *mech = NULL;
        char                    *target = NULL;
        char                    *service = NULL;
+       char                    *enctypes = NULL;
 
        printerr(1, "handling gssd upcall (%s)\n", clp->dirname);
 
@@ -1162,6 +1238,23 @@ handle_gssd_upcall(struct clnt_info *clp)
                goto out;
        }
 
+       /* read supported encryption types if supplied */
+       if ((p = strstr(lbuf, "enctypes=")) != NULL) {
+               enctypes = malloc(lbuflen);
+               if (!enctypes)
+                       goto out;
+               if (sscanf(p, "enctypes=%s", enctypes) != 1) {
+                       printerr(0, "WARNING: handle_gssd_upcall: "
+                                   "failed to parse encryption types "
+                                   "in upcall string '%s'\n", lbuf);
+                       goto out;
+               }
+               if (parse_enctypes(enctypes) != 0) {
+                       printerr(0, "WARNING: handle_gssd_upcall: "
+                               "parsing encryption types failed: errno %d\n", errno);
+               }
+       }
+
        /* read target name */
        if ((p = strstr(lbuf, "target=")) != NULL) {
                target = malloc(lbuflen);
@@ -1208,6 +1301,7 @@ handle_gssd_upcall(struct clnt_info *clp)
 out:
        free(lbuf);
        free(mech);
+       free(enctypes);
        free(target);
        free(service);
        return;