blob: 2503c384c50beb33dd504386c8ea39a88fc02d5c [file] [log] [blame]
/*
* COPYRIGHT (c) 2011
* The Regents of the University of Michigan
* ALL RIGHTS RESERVED
*
* Permission is granted to use, copy, create derivative works
* and redistribute this software and such derivative works
* for any purpose, so long as the name of The University of
* Michigan is not used in any advertising or publicity
* pertaining to the use of distribution of this software
* without specific, written prior authorization. If the
* above copyright notice or any other identification of the
* University of Michigan is included in any copy of any
* portion of this software, then the disclaimer below must
* also be included.
*
* THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
* FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
* PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
* MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
* WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
* REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
* FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
* CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
* OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
* IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGES.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <gssapi/gssapi.h>
#include <krb5.h>
#include "gss_util.h"
#include "gss_oids.h"
#include "err_util.h"
#include "svcgssd_krb5.h"
#include "version.h"
#define MYBUFLEN 1024
char *supported_enctypes_filename = "/proc/fs/nfsd/supported_krb5_enctypes";
int parsed_num_enctypes = 0;
krb5_enctype *parsed_enctypes = NULL;
char *cached_enctypes = NULL;
/*==========================*/
/*=== Internal routines ===*/
/*==========================*/
/*
* Parse the supported encryption type information
*/
static int
parse_enctypes(char *enctypes)
{
int n = 0;
char *curr, *comma;
int i;
/* Don't parse the same string over and over... */
if (cached_enctypes && strcmp(cached_enctypes, enctypes) == 0)
return 0;
/* Free any existing cached_enctypes */
svcgssd_free_enctypes();
/* 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;
/* Skip pass any non digits */
while (*enctypes && isdigit(*enctypes) == 0)
enctypes++;
if (*enctypes == '\0')
return EINVAL;
/* Allocate space for enctypes array */
if ((parsed_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) {
parsed_enctypes[i++] = atoi(curr);
comma = strchr(curr, ',');
if (comma == NULL)
break;
}
parsed_num_enctypes = n;
if ((cached_enctypes = malloc(strlen(enctypes)+1)))
strcpy(cached_enctypes, enctypes);
return 0;
}
static void
get_kernel_supported_enctypes(void)
{
FILE *s_e;
int ret;
char buffer[MYBUFLEN + 1];
memset(buffer, '\0', sizeof(buffer));
s_e = fopen(supported_enctypes_filename, "r");
if (s_e == NULL)
goto out_clean_parsed;
ret = fread(buffer, 1, MYBUFLEN, s_e);
if (ret < 0) {
fclose(s_e);
goto out_clean_parsed;
}
fclose(s_e);
if (parse_enctypes(buffer)) {
goto out_clean_parsed;
}
out:
return;
out_clean_parsed:
if (parsed_enctypes != NULL) {
free(parsed_enctypes);
parsed_num_enctypes = 0;
}
goto out;
}
/*==========================*/
/*=== External routines ===*/
/*==========================*/
void
svcgssd_free_enctypes(void)
{
free(cached_enctypes);
cached_enctypes = NULL;
if (parsed_enctypes != NULL) {
free(parsed_enctypes);
parsed_enctypes = NULL;
parsed_num_enctypes = 0;
}
}
/*
* Get encryption types supported by the kernel, and then
* call gss_krb5_set_allowable_enctypes() to limit the
* encryption types negotiated.
*
* Returns:
* 0 => all went well
* -1 => there was an error
*/
int
svcgssd_limit_krb5_enctypes(void)
{
#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
u_int maj_stat, min_stat;
krb5_enctype old_kernel_enctypes[] = {
ENCTYPE_DES_CBC_CRC,
ENCTYPE_DES_CBC_MD5,
ENCTYPE_DES_CBC_MD4 };
krb5_enctype new_kernel_enctypes[] = {
ENCTYPE_AES256_CTS_HMAC_SHA1_96,
ENCTYPE_AES128_CTS_HMAC_SHA1_96,
ENCTYPE_DES3_CBC_SHA1,
ENCTYPE_ARCFOUR_HMAC,
ENCTYPE_DES_CBC_CRC,
ENCTYPE_DES_CBC_MD5,
ENCTYPE_DES_CBC_MD4 };
krb5_enctype *default_enctypes, *enctypes;
int default_num_enctypes, num_enctypes;
if (linux_version_code() < MAKE_VERSION(2, 6, 35)) {
default_enctypes = old_kernel_enctypes;
default_num_enctypes =
sizeof(old_kernel_enctypes) / sizeof(old_kernel_enctypes[0]);
} else {
default_enctypes = new_kernel_enctypes;
default_num_enctypes =
sizeof(new_kernel_enctypes) / sizeof(new_kernel_enctypes[0]);
}
get_kernel_supported_enctypes();
if (parsed_enctypes != NULL) {
enctypes = parsed_enctypes;
num_enctypes = parsed_num_enctypes;
printerr(2, "%s: Calling gss_set_allowable_enctypes with %d "
"enctypes from the kernel\n", __func__, num_enctypes);
} else {
enctypes = default_enctypes;
num_enctypes = default_num_enctypes;
printerr(2, "%s: Calling gss_set_allowable_enctypes with %d "
"enctypes from defaults\n", __func__, num_enctypes);
}
maj_stat = gss_set_allowable_enctypes(&min_stat, gssd_creds,
&krb5oid, num_enctypes, enctypes);
if (maj_stat != GSS_S_COMPLETE) {
printerr(1, "WARNING: gss_set_allowable_enctypes failed\n");
pgsserr("svcgssd_limit_krb5_enctypes: gss_set_allowable_enctypes",
maj_stat, min_stat, &krb5oid);
return -1;
}
#endif
return 0;
}