xref: /freebsd/crypto/krb5/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_services.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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