1 /*
2  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * lib/kdb/kdb_ldap/ldap_service_stash.c
8  *
9  * Copyright (c) 2004-2005, Novell, Inc.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions are met:
14  *
15  *   * Redistributions of source code must retain the above copyright notice,
16  *       this list of conditions and the following disclaimer.
17  *   * Redistributions in binary form must reproduce the above copyright
18  *       notice, this list of conditions and the following disclaimer in the
19  *       documentation and/or other materials provided with the distribution.
20  *   * The copyright holder's name is not used to endorse or promote products
21  *       derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include <ctype.h>
37 #include "ldap_main.h"
38 #include "kdb_ldap.h"
39 #include "ldap_service_stash.h"
40 #include <libintl.h>
41 
42 krb5_error_code
krb5_ldap_readpassword(context,ldap_context,password)43 krb5_ldap_readpassword(context, ldap_context, password)
44     krb5_context                context;
45     krb5_ldap_context           *ldap_context;
46     unsigned char               **password;
47 {
48     int                         entryfound=0;
49     krb5_error_code             st=0;
50     char                        line[RECORDLEN]="0", *start=NULL, *file=NULL;
51     char                        errbuf[1024];
52     FILE                        *fptr=NULL;
53 
54     *password = NULL;
55 
56     if (ldap_context->service_password_file)
57 	file = ldap_context->service_password_file;
58 
59 #ifndef HAVE_STRERROR_R
60 # undef strerror_r
61     /* Solaris Kerberos: safer macro, added more (), use strlcpy() */
62 # define strerror_r(ERRNUM, BUF, SIZE) (strlcpy((BUF), strerror(ERRNUM), (SIZE)), (BUF)[(SIZE)-1] = 0)
63 #endif
64 
65     /* Solaris Kerberos: access()'s are unsafe and useless */
66 #if 0 /************** Begin IFDEF'ed OUT *******************************/
67     /* check whether file exists */
68     if (access(file, F_OK) < 0) {
69 	st = errno;
70 	strerror_r(errno, errbuf, sizeof(errbuf));
71 	krb5_set_error_message (context, st, "%s", errbuf);
72 	goto rp_exit;
73     }
74 
75     /* check read access */
76     if (access(file, R_OK) < 0) {
77 	st = errno;
78 	strerror_r(errno, errbuf, sizeof(errbuf));
79 	krb5_set_error_message (context, st, "%s", errbuf);
80 	goto rp_exit;
81     }
82 #endif /**************** END IFDEF'ed OUT *******************************/
83 
84     /* Solaris Kerberos: using F to deal with 256 open file limit */
85     if ((fptr = fopen(file, "rF")) == NULL) {
86 	st = errno;
87 	strerror_r(errno, errbuf, sizeof(errbuf));
88 	krb5_set_error_message (context, st, "%s", errbuf);
89 	goto rp_exit;
90     }
91 
92     /* get the record from the file */
93     while (fgets(line, RECORDLEN, fptr) != NULL) {
94 	char tmp[RECORDLEN];
95 
96 	tmp[0] = '\0';
97 	/* Handle leading white-spaces */
98 	for (start = line; isspace(*start); ++start);
99 
100 	/* Handle comment lines */
101 	if (*start == '!' || *start == '#')
102 	    continue;
103 	sscanf(line, "%*[ \t]%[^#]", tmp);
104 	if (tmp[0] == '\0')
105 	    sscanf(line, "%[^#]", tmp);
106 	if (strcasecmp(tmp, ldap_context->bind_dn) == 0) {
107 	    entryfound = 1; /* service_dn record found !!! */
108 	    break;
109 	}
110     }
111     (void) fclose (fptr);
112 
113     if (entryfound == 0)  {
114 	st = KRB5_KDB_SERVER_INTERNAL_ERR;
115 	krb5_set_error_message (context, st, gettext("Bind DN entry missing in stash file"));
116 	goto rp_exit;
117     }
118     /* replace the \n with \0 */
119     start = strchr(line, '\n');
120     if (start)
121 	*start = '\0';
122 
123     start = strchr(line, '#');
124     if (start == NULL) {
125 	/* password field missing */
126 	st = KRB5_KDB_SERVER_INTERNAL_ERR;
127 	krb5_set_error_message (context, st, gettext("Stash file entry corrupt"));
128 	goto rp_exit;
129     }
130     ++ start;
131     /* Extract the plain password / certificate file information */
132     {
133 	struct data PT, CT;
134 
135 	/* Check if the entry has the path of a certificate */
136 	if (!strncmp(start, "{FILE}", strlen("{FILE}"))) {
137 	    /* Set *password = {FILE}<path to cert>\0<cert password> */
138 	    /*ptr = strchr(start, ':');
139 	      if (ptr == NULL) { */
140 	    *password = (unsigned char *)malloc(strlen(start) + 2);
141 	    if (*password == NULL) {
142 		st = ENOMEM;
143 		goto rp_exit;
144 	    }
145 	    (*password)[strlen(start) + 1] = '\0';
146 	    (*password)[strlen(start)] = '\0';
147 	    /*LINTED*/
148 	    strcpy((char *)(*password), start);
149 	    goto got_password;
150 	} else {
151 	    CT.value = (unsigned char *)start;
152 	    CT.len = strlen((char *)CT.value);
153 	    st = dec_password(CT, &PT);
154 	    if (st != 0) {
155 		switch (st) {
156 		case ERR_NO_MEM:
157 		    st = ENOMEM;
158 		    break;
159 		case ERR_PWD_ZERO:
160 		    st = EINVAL;
161 		    krb5_set_error_message(context, st, gettext("Password has zero length"));
162 		    break;
163 		case ERR_PWD_BAD:
164 		    st = EINVAL;
165 		    krb5_set_error_message(context, st, gettext("Password corrupted"));
166 		    break;
167 		case ERR_PWD_NOT_HEX:
168 		    st = EINVAL;
169 		    krb5_set_error_message(context, st, gettext("Not a hexadecimal password"));
170 		    break;
171 		default:
172 		    st = KRB5_KDB_SERVER_INTERNAL_ERR;
173 		    break;
174 		}
175 		goto rp_exit;
176 	    }
177 	    *password = PT.value;
178 	}
179     }
180 got_password:
181 
182 rp_exit:
183     if (st) {
184 	if (*password)
185 	    free (*password);
186 	*password = NULL;
187     }
188     return st;
189 }
190 
191 /* Encodes a sequence of bytes in hexadecimal */
192 
193 int
tohex(in,ret)194 tohex(in, ret)
195     krb5_data         in;
196     krb5_data         *ret;
197 {
198     int                i=0, err = 0;
199 
200     ret->length = 0;
201     ret->data = NULL;
202 
203     ret->data = malloc((unsigned int)in.length * 2 + 1 /*Null termination */);
204     if (ret->data == NULL) {
205 	err = ENOMEM;
206 	goto cleanup;
207     }
208     ret->length = in.length * 2;
209     ret->data[ret->length] = 0;
210 
211     for (i = 0; i < in.length; i++)
212 	sprintf(ret->data + (2 * i), "%02x", in.data[i] & 0xff);
213 
214 cleanup:
215 
216     if (ret->length == 0) {
217 	free(ret->data);
218 	ret->data = NULL;
219     }
220 
221     return err;
222 }
223 
224 /* The entry in the password file will have the following format
225  * <FQDN of service> = <secret>
226  *   <secret> := {HEX}<password in hexadecimal>
227  *
228  * <password> is the actual eDirectory password of the service
229  * Return values:
230  * ERR_NO_MEM      - No Memory
231  * ERR_PWD_ZERO    - Password has zero length
232  * ERR_PWD_BAD     - Passowrd corrupted
233  * ERR_PWD_NOT_HEX - Not a hexadecimal password
234  */
235 
dec_password(struct data pwd,struct data * ret)236 int dec_password(struct data pwd, struct data *ret) {
237     int err=0;
238     int i=0, j=0;
239 
240     ret->len = 0;
241     ret->value = NULL;
242 
243     if (pwd.len == 0) {
244 	err = ERR_PWD_ZERO;
245 	ret->len = 0;
246 	goto cleanup;
247     }
248 
249     /* Check if it is a hexadecimal encoded password */
250     if (pwd.len >= strlen("{HEX}") &&
251 	strncmp((char *)pwd.value, "{HEX}", strlen("{HEX}")) == 0) {
252 
253 	if ((pwd.len - strlen("{HEX}")) % 2 != 0) {
254 	    /* A hexadecimal encoded password should have even length */
255 	    err = ERR_PWD_BAD;
256 	    ret->len = 0;
257 	    goto cleanup;
258 	}
259 	ret->value = (unsigned char *)malloc((pwd.len - strlen("{HEX}")) / 2 + 1);
260 	if (ret->value == NULL) {
261 	    err = ERR_NO_MEM;
262 	    ret->len = 0;
263 	    goto cleanup;
264 	}
265 	ret->len = (pwd.len - strlen("{HEX}")) / 2;
266 	ret->value[ret->len] = '\0';
267 	for (i = strlen("{HEX}"), j = 0; i < pwd.len; i += 2, j++) {
268 	    unsigned int k;
269 	    /* Check if it is a hexadecimal number */
270 	    if (isxdigit(pwd.value[i]) == 0 || isxdigit(pwd.value[i + 1]) == 0) {
271 		err = ERR_PWD_NOT_HEX;
272 		ret->len = 0;
273 		goto cleanup;
274 	    }
275 	    sscanf((char *)pwd.value + i, "%2x", &k);
276 	    ret->value[j] = k;
277 	}
278 	goto cleanup;
279     } else {
280 	err = ERR_PWD_NOT_HEX;
281 	ret->len = 0;
282 	goto cleanup;
283     }
284 
285 cleanup:
286 
287     if (ret->len == 0) {
288 	free(ret->value);
289 	ret->value = NULL;
290     }
291     return(err);
292 }
293