2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * lib/kdb/kdb_ldap/ldap_service_stash.c
2N/A *
2N/A * Copyright (c) 2004-2005, Novell, Inc.
2N/A * All rights reserved.
2N/A *
2N/A * Redistribution and use in source and binary forms, with or without
2N/A * modification, are permitted provided that the following conditions are met:
2N/A *
2N/A * * Redistributions of source code must retain the above copyright notice,
2N/A * this list of conditions and the following disclaimer.
2N/A * * Redistributions in binary form must reproduce the above copyright
2N/A * notice, this list of conditions and the following disclaimer in the
2N/A * documentation and/or other materials provided with the distribution.
2N/A * * The copyright holder's name is not used to endorse or promote products
2N/A * derived from this software without specific prior written permission.
2N/A *
2N/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2N/A * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2N/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2N/A * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
2N/A * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2N/A * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2N/A * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2N/A * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2N/A * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2N/A * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2N/A * POSSIBILITY OF SUCH DAMAGE.
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <ctype.h>
2N/A#include "ldap_main.h"
2N/A#include "kdb_ldap.h"
2N/A#include "ldap_service_stash.h"
2N/A#include <libintl.h> /* Solaris Kerberos */
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_readpassword(krb5_context context, krb5_ldap_context *ldap_context,
2N/A unsigned char **password)
2N/A{
2N/A int entryfound=0;
2N/A krb5_error_code st=0;
2N/A char line[RECORDLEN]="0", *start=NULL, *file=NULL;
2N/A char errbuf[1024];
2N/A FILE *fptr=NULL;
2N/A
2N/A *password = NULL;
2N/A
2N/A if (ldap_context->service_password_file)
2N/A file = ldap_context->service_password_file;
2N/A
2N/A#ifndef HAVE_STRERROR_R
2N/A# undef strerror_r
2N/A /* Solaris Kerberos: safer macro, added more (), use strlcpy() */
2N/A# define strerror_r(ERRNUM, BUF, SIZE) (strlcpy((BUF), strerror(ERRNUM), (SIZE)), (BUF)[(SIZE)-1] = 0)
2N/A#endif
2N/A
2N/A /* Solaris Kerberos: access()'s are unsafe and useless */
2N/A#if 0 /************** Begin IFDEF'ed OUT *******************************/
2N/A /* check whether file exists */
2N/A if (access(file, F_OK) < 0) {
2N/A st = errno;
2N/A strerror_r(errno, errbuf, sizeof(errbuf));
2N/A krb5_set_error_message (context, st, "%s", errbuf);
2N/A goto rp_exit;
2N/A }
2N/A
2N/A /* check read access */
2N/A if (access(file, R_OK) < 0) {
2N/A st = errno;
2N/A strerror_r(errno, errbuf, sizeof(errbuf));
2N/A krb5_set_error_message (context, st, "%s", errbuf);
2N/A goto rp_exit;
2N/A }
2N/A#endif /**************** END IFDEF'ed OUT *******************************/
2N/A
2N/A /* Solaris Kerberos: using F to deal with 256 open file limit */
2N/A if ((fptr = fopen(file, "rF")) == NULL) {
2N/A st = errno;
2N/A strerror_r(errno, errbuf, sizeof(errbuf));
2N/A krb5_set_error_message (context, st, "%s", errbuf);
2N/A goto rp_exit;
2N/A }
2N/A set_cloexec_file(fptr);
2N/A
2N/A /* get the record from the file */
2N/A while (fgets(line, RECORDLEN, fptr)!= NULL) {
2N/A char tmp[RECORDLEN];
2N/A
2N/A tmp[0] = '\0';
2N/A /* Handle leading white-spaces */
2N/A for (start = line; isspace(*start); ++start);
2N/A
2N/A /* Handle comment lines */
2N/A if (*start == '!' || *start == '#')
2N/A continue;
2N/A sscanf(line, "%*[ \t]%[^#]", tmp);
2N/A if (tmp[0] == '\0')
2N/A sscanf(line, "%[^#]", tmp);
2N/A if (strcasecmp(tmp, ldap_context->bind_dn) == 0) {
2N/A entryfound = 1; /* service_dn record found !!! */
2N/A break;
2N/A }
2N/A }
2N/A fclose (fptr);
2N/A
2N/A if (entryfound == 0) {
2N/A st = KRB5_KDB_SERVER_INTERNAL_ERR;
2N/A krb5_set_error_message (context, st, gettext("Bind DN entry missing in stash file"));
2N/A goto rp_exit;
2N/A }
2N/A /* replace the \n with \0 */
2N/A start = strchr(line, '\n');
2N/A if (start)
2N/A *start = '\0';
2N/A
2N/A start = strchr(line, '#');
2N/A if (start == NULL) {
2N/A /* password field missing */
2N/A st = KRB5_KDB_SERVER_INTERNAL_ERR;
2N/A krb5_set_error_message (context, st, gettext("Stash file entry corrupt"));
2N/A goto rp_exit;
2N/A }
2N/A ++ start;
2N/A /* Extract the plain password / certificate file information */
2N/A {
2N/A struct data PT, CT;
2N/A
2N/A /* Check if the entry has the path of a certificate */
2N/A if (!strncmp(start, "{FILE}", strlen("{FILE}"))) {
2N/A /* Set *password = {FILE}<path to cert>\0<cert password> */
2N/A size_t len = strlen(start);
2N/A
2N/A *password = (unsigned char *)malloc(len + 2);
2N/A if (*password == NULL) {
2N/A st = ENOMEM;
2N/A goto rp_exit;
2N/A }
2N/A memcpy(*password, start, len);
2N/A (*password)[len] = '\0';
2N/A (*password)[len + 1] = '\0';
2N/A goto got_password;
2N/A } else {
2N/A CT.value = (unsigned char *)start;
2N/A CT.len = strlen((char *)CT.value);
2N/A st = dec_password(CT, &PT);
2N/A if (st != 0) {
2N/A switch (st) {
2N/A case ERR_NO_MEM:
2N/A st = ENOMEM;
2N/A break;
2N/A case ERR_PWD_ZERO:
2N/A st = EINVAL;
2N/A krb5_set_error_message(context, st, gettext("Password has zero length"));
2N/A break;
2N/A case ERR_PWD_BAD:
2N/A st = EINVAL;
2N/A krb5_set_error_message(context, st, gettext("Password corrupted"));
2N/A break;
2N/A case ERR_PWD_NOT_HEX:
2N/A st = EINVAL;
2N/A krb5_set_error_message(context, st, gettext("Not a hexadecimal password"));
2N/A break;
2N/A default:
2N/A st = KRB5_KDB_SERVER_INTERNAL_ERR;
2N/A break;
2N/A }
2N/A goto rp_exit;
2N/A }
2N/A *password = PT.value;
2N/A }
2N/A }
2N/Agot_password:
2N/A
2N/Arp_exit:
2N/A if (st) {
2N/A if (*password)
2N/A free (*password);
2N/A *password = NULL;
2N/A }
2N/A return st;
2N/A}
2N/A
2N/A/* Encodes a sequence of bytes in hexadecimal */
2N/A
2N/Aint
2N/Atohex(krb5_data in, krb5_data *ret)
2N/A{
2N/A int i=0, err = 0;
2N/A
2N/A ret->length = 0;
2N/A ret->data = NULL;
2N/A
2N/A ret->data = malloc((unsigned int)in.length * 2 + 1 /*Null termination */);
2N/A if (ret->data == NULL) {
2N/A err = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A ret->length = in.length * 2;
2N/A ret->data[ret->length] = 0;
2N/A
2N/A for (i = 0; i < in.length; i++)
2N/A snprintf(ret->data + 2 * i, 3, "%02x", in.data[i] & 0xff);
2N/A
2N/Acleanup:
2N/A
2N/A if (ret->length == 0) {
2N/A free(ret->data);
2N/A ret->data = NULL;
2N/A }
2N/A
2N/A return err;
2N/A}
2N/A
2N/A/* The entry in the password file will have the following format
2N/A * <FQDN of service> = <secret>
2N/A * <secret> := {HEX}<password in hexadecimal>
2N/A *
2N/A * <password> is the actual eDirectory password of the service
2N/A * Return values:
2N/A * ERR_NO_MEM - No Memory
2N/A * ERR_PWD_ZERO - Password has zero length
2N/A * ERR_PWD_BAD - Passowrd corrupted
2N/A * ERR_PWD_NOT_HEX - Not a hexadecimal password
2N/A */
2N/A
2N/Aint
2N/Adec_password(struct data pwd, struct data *ret)
2N/A{
2N/A int err=0;
2N/A int i=0, j=0;
2N/A
2N/A ret->len = 0;
2N/A ret->value = NULL;
2N/A
2N/A if (pwd.len == 0) {
2N/A err = ERR_PWD_ZERO;
2N/A ret->len = 0;
2N/A goto cleanup;
2N/A }
2N/A
2N/A /* Check if it is a hexadecimal encoded password */
2N/A if (pwd.len >= strlen("{HEX}") &&
2N/A strncmp((char *)pwd.value, "{HEX}", strlen("{HEX}")) == 0) {
2N/A
2N/A if ((pwd.len - strlen("{HEX}")) % 2 != 0) {
2N/A /* A hexadecimal encoded password should have even length */
2N/A err = ERR_PWD_BAD;
2N/A ret->len = 0;
2N/A goto cleanup;
2N/A }
2N/A ret->value = (unsigned char *)malloc((pwd.len - strlen("{HEX}")) / 2 + 1);
2N/A if (ret->value == NULL) {
2N/A err = ERR_NO_MEM;
2N/A ret->len = 0;
2N/A goto cleanup;
2N/A }
2N/A ret->len = (pwd.len - strlen("{HEX}")) / 2;
2N/A ret->value[ret->len] = '\0';
2N/A for (i = strlen("{HEX}"), j = 0; i < pwd.len; i += 2, j++) {
2N/A unsigned int k;
2N/A /* Check if it is a hexadecimal number */
2N/A if (isxdigit(pwd.value[i]) == 0 || isxdigit(pwd.value[i + 1]) == 0) {
2N/A err = ERR_PWD_NOT_HEX;
2N/A ret->len = 0;
2N/A goto cleanup;
2N/A }
2N/A sscanf((char *)pwd.value + i, "%2x", &k);
2N/A ret->value[j] = k;
2N/A }
2N/A goto cleanup;
2N/A } else {
2N/A err = ERR_PWD_NOT_HEX;
2N/A ret->len = 0;
2N/A goto cleanup;
2N/A }
2N/A
2N/Acleanup:
2N/A
2N/A if (ret->len == 0) {
2N/A free(ret->value);
2N/A ret->value = NULL;
2N/A }
2N/A return(err);
2N/A}