1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* plugins/kdb/ldap/ldap_util/kdb5_ldap_services.c */
3 /* Copyright (c) 2004-2005, Novell, Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * The copyright holder's name is not used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 /*
31 * Create / Delete / Modify / View / List service objects.
32 */
33
34 /*
35 * Service objects have rights over realm objects and principals. The following
36 * functions manage the service objects.
37 */
38
39 #include <k5-int.h>
40 #include <k5-hex.h>
41 #include "kdb5_ldap_util.h"
42 #include "kdb5_ldap_list.h"
43
44 /* Get the configured LDAP service password file. The caller should free the
45 * result with profile_release_string(). */
46 static krb5_error_code
get_conf_service_file(profile_t profile,const char * realm,char ** path_out)47 get_conf_service_file(profile_t profile, const char *realm, char **path_out)
48 {
49 char *subsection, *path;
50 long ret;
51
52 *path_out = NULL;
53
54 /* Get the [dbmodules] subsection for realm. */
55 ret = profile_get_string(profile, KDB_REALM_SECTION, realm,
56 KDB_MODULE_POINTER, realm, &subsection);
57 if (ret)
58 return ret;
59
60 /* Look up the password file in the [dbmodules] subsection. */
61 ret = profile_get_string(profile, KDB_MODULE_SECTION, subsection,
62 KRB5_CONF_LDAP_SERVICE_PASSWORD_FILE, NULL,
63 &path);
64 profile_release_string(subsection);
65 if (ret)
66 return ret;
67
68 if (path == NULL) {
69 /* Look up the password file in [dbdefaults] as a fallback. */
70 ret = profile_get_string(profile, KDB_MODULE_DEF_SECTION,
71 KRB5_CONF_LDAP_SERVICE_PASSWORD_FILE, NULL,
72 NULL, &path);
73 if (ret)
74 return ret;
75 }
76
77 if (path == NULL) {
78 k5_setmsg(util_context, ENOENT,
79 _("ldap_service_password_file not configured"));
80 return ENOENT;
81 }
82
83 *path_out = path;
84 return 0;
85 }
86
87 /*
88 * Convert the user supplied password into hexadecimal and stash it. Only a
89 * little more secure than storing plain password in the file ...
90 */
91 void
kdb5_ldap_stash_service_password(int argc,char ** argv)92 kdb5_ldap_stash_service_password(int argc, char **argv)
93 {
94 int ret = 0;
95 unsigned int passwd_len = 0;
96 char *me = progname;
97 char *service_object = NULL;
98 char *file_name = NULL, *tmp_file = NULL;
99 char passwd[MAX_SERVICE_PASSWD_LEN];
100 char *str = NULL, *hexpasswd = NULL;
101 char line[MAX_LEN];
102 FILE *pfile = NULL;
103 krb5_boolean print_usage = FALSE;
104 mode_t old_mode = 0;
105
106 /*
107 * Format:
108 * stashsrvpw [-f filename] service_dn
109 * where
110 * 'service_dn' is the DN of the service object
111 * 'filename' is the path of the stash file
112 */
113 if (argc != 2 && argc != 4) {
114 print_usage = TRUE;
115 goto cleanup;
116 }
117
118 if (argc == 4) {
119 /* Find the stash file name */
120 if (strcmp (argv[1], "-f") == 0) {
121 if (((file_name = strdup (argv[2])) == NULL) ||
122 ((service_object = strdup (argv[3])) == NULL)) {
123 com_err(me, ENOMEM,
124 _("while setting service object password"));
125 goto cleanup;
126 }
127 } else if (strcmp (argv[2], "-f") == 0) {
128 if (((file_name = strdup (argv[3])) == NULL) ||
129 ((service_object = strdup (argv[1])) == NULL)) {
130 com_err(me, ENOMEM,
131 _("while setting service object password"));
132 goto cleanup;
133 }
134 } else {
135 print_usage = TRUE;
136 goto cleanup;
137 }
138 } else { /* argc == 2 */
139 service_object = strdup (argv[1]);
140 if (service_object == NULL) {
141 com_err(me, ENOMEM, _("while setting service object password"));
142 goto cleanup;
143 }
144
145 ret = get_conf_service_file(util_context->profile,
146 util_context->default_realm, &file_name);
147 if (ret) {
148 com_err(me, ret, _("while getting service password filename"));
149 goto cleanup;
150 }
151 }
152
153 /* Get password from user */
154 {
155 char prompt1[256], prompt2[256];
156
157 /* Get the service object password from the terminal */
158 memset(passwd, 0, sizeof (passwd));
159 passwd_len = sizeof (passwd);
160
161 snprintf(prompt1, sizeof(prompt1), _("Password for \"%s\""),
162 service_object);
163
164 snprintf(prompt2, sizeof(prompt2), _("Re-enter password for \"%s\""),
165 service_object);
166
167 ret = krb5_read_password(util_context, prompt1, prompt2, passwd, &passwd_len);
168 if (ret != 0) {
169 com_err(me, ret, _("while setting service object password"));
170 memset(passwd, 0, sizeof (passwd));
171 goto cleanup;
172 }
173
174 if (passwd_len == 0) {
175 printf(_("%s: Invalid password\n"), me);
176 memset(passwd, 0, MAX_SERVICE_PASSWD_LEN);
177 goto cleanup;
178 }
179 }
180
181 /* Convert the password to hexadecimal */
182 ret = k5_hex_encode(passwd, passwd_len, FALSE, &hexpasswd);
183 zap(passwd, passwd_len);
184 if (ret != 0) {
185 com_err(me, ret, _("Failed to convert the password to hexadecimal"));
186 goto cleanup;
187 }
188
189 /* TODO: file lock for the service password file */
190
191 /* set password in the file */
192 old_mode = umask(0177);
193 pfile = fopen(file_name, "a+");
194 if (pfile == NULL) {
195 com_err(me, errno, _("Failed to open file %s: %s"), file_name,
196 strerror (errno));
197 goto cleanup;
198 }
199 set_cloexec_file(pfile);
200 rewind (pfile);
201 umask(old_mode);
202
203 while (fgets (line, MAX_LEN, pfile) != NULL) {
204 if ((str = strstr (line, service_object)) != NULL) {
205 /* White spaces not allowed */
206 if (line [strlen (service_object)] == '#')
207 break;
208 str = NULL;
209 }
210 }
211
212 if (str == NULL) {
213 if (feof(pfile)) {
214 /* If the service object dn is not present in the service password file */
215 if (fprintf(pfile, "%s#{HEX}%s\n", service_object, hexpasswd) < 0) {
216 com_err(me, errno,
217 _("Failed to write service object password to file"));
218 fclose(pfile);
219 goto cleanup;
220 }
221 } else {
222 com_err(me, errno,
223 _("Error reading service object password file"));
224 fclose(pfile);
225 goto cleanup;
226 }
227 fclose(pfile);
228 } else {
229 /*
230 * Password entry for the service object is already present in the file
231 * Delete the existing entry and add the new entry
232 */
233 FILE *newfile;
234
235 mode_t omask;
236
237 /* Create a new file with the extension .tmp */
238 if (asprintf(&tmp_file,"%s.tmp",file_name) < 0) {
239 com_err(me, ENOMEM, _("while setting service object password"));
240 fclose(pfile);
241 goto cleanup;
242 }
243
244 omask = umask(077);
245 newfile = fopen(tmp_file, "w");
246 umask (omask);
247 if (newfile == NULL) {
248 com_err(me, errno, _("Error creating file %s"), tmp_file);
249 fclose(pfile);
250 goto cleanup;
251 }
252 set_cloexec_file(newfile);
253
254 fseek(pfile, 0, SEEK_SET);
255 while (fgets(line, MAX_LEN, pfile) != NULL) {
256 if (((str = strstr(line, service_object)) != NULL) &&
257 (line[strlen(service_object)] == '#')) {
258 if (fprintf(newfile, "%s#{HEX}%s\n", service_object, hexpasswd) < 0) {
259 com_err(me, errno, _("Failed to write service object "
260 "password to file"));
261 fclose(newfile);
262 unlink(tmp_file);
263 fclose(pfile);
264 goto cleanup;
265 }
266 } else {
267 if (fprintf (newfile, "%s", line) < 0) {
268 com_err(me, errno, _("Failed to write service object "
269 "password to file"));
270 fclose(newfile);
271 unlink(tmp_file);
272 fclose(pfile);
273 goto cleanup;
274 }
275 }
276 }
277
278 if (!feof(pfile)) {
279 com_err(me, errno,
280 _("Error reading service object password file"));
281 fclose(newfile);
282 unlink(tmp_file);
283 fclose(pfile);
284 goto cleanup;
285 }
286
287 /* TODO: file lock for the service password file */
288
289 fclose(pfile);
290 fclose(newfile);
291
292 ret = rename(tmp_file, file_name);
293 if (ret != 0) {
294 com_err(me, errno,
295 _("Failed to write service object password to file"));
296 goto cleanup;
297 }
298 }
299 ret = 0;
300
301 cleanup:
302
303 zapfreestr(hexpasswd);
304
305 if (service_object)
306 free(service_object);
307
308 profile_release_string(file_name);
309
310 if (tmp_file)
311 free(tmp_file);
312
313 if (print_usage)
314 usage();
315 /* db_usage(STASH_SRV_PW); */
316
317 if (ret)
318 exit_status++;
319 }
320