Bug Summary

File:client/ipa-join.c
Warning:line 750, column 9
Value stored to 'krberr' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name ipa-join.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mthread-model posix -mframe-pointer=all -fmath-errno -fno-rounding-math -masm-verbose -mconstructor-aliases -munwind-tables -target-cpu x86-64 -dwarf-column-info -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/10.0.0 -D HAVE_CONFIG_H -I . -I .. -I . -I ../util -I ../asn1 -D PREFIX="/usr/local" -D BINDIR="/usr/local/bin" -D LIBDIR="/usr/local/lib" -D LIBEXECDIR="/usr/local/libexec" -D DATADIR="/usr/local/share" -D LOCALEDIR="/usr/local/share/locale" -D IPACONFFILE="/usr/local/etc/ipa/default.conf" -I /usr/include/nspr4 -I /usr/include/nss3 -I /usr/include/nspr4 -D __STDC_WANT_LIB_EXT1__=1 -D _DEFAULT_SOURCE=1 -D _POSIX_C_SOURCE=200809L -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/10.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdebug-compilation-dir /home/heimes/redhat/freeipa/client -ferror-limit 19 -fmessage-length 0 -fgnuc-version=4.2.1 -fobjc-runtime=gcc -fdiagnostics-show-option -analyzer-output=html -faddrsig -o /home/heimes/redhat/freeipa/report/2020-06-05-101548-295465-1 -x c ipa-join.c
1/* Authors: Rob Crittenden <rcritten@redhat.com>
2 *
3 * Copyright (C) 2009 Red Hat
4 * see file 'COPYING' for use and warranty information
5 *
6 * This program is free software you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#define _GNU_SOURCE
21
22#include "config.h"
23#include <unistd.h>
24#include <stdlib.h>
25#include <stdio.h>
26#include <string.h>
27#include <errno(*__errno_location ()).h>
28#include <assert.h>
29#include <sys/utsname.h>
30#include <krb5.h>
31/* Doesn't work w/mozldap */
32#include <ldap.h>
33#include <popt.h>
34#include <sys/stat.h>
35#include <fcntl.h>
36#include <sys/wait.h>
37
38#include "xmlrpc-c/base.h"
39#include "xmlrpc-c/client.h"
40
41#include "ipa-client-common.h"
42#include "ipa_ldap.h"
43
44#define NAME"ipa-join" "ipa-join"
45
46#define JOIN_OID"2.16.840.1.113730.3.8.10.3" "2.16.840.1.113730.3.8.10.3"
47
48#define IPA_CONFIG"/etc/ipa/default.conf" "/etc/ipa/default.conf"
49
50char * read_config_file(const char *filename);
51char * get_config_entry(char * data, const char *section, const char *key);
52
53static int debug = 0;
54
55/*
56 * Translate some IPA exceptions into specific errors in this context.
57 */
58static int
59handle_fault(xmlrpc_env * const envP) {
60 if (envP->fault_occurred) {
61 switch(envP->fault_code) {
62 case 2100: /* unable to add new host entry or write objectClass */
63 fprintf(stderrstderr,
64 _("No permission to join this host to the IPA domain.\n")gettext("No permission to join this host to the IPA domain.\n"
)
);
65 break;
66 default:
67 fprintf(stderrstderr, "%s\n", envP->fault_string);
68 }
69 return 1;
70 }
71 return 0;
72}
73
74/* Get the IPA server from the configuration file.
75 * The caller is responsible for freeing this value
76 */
77static char *
78getIPAserver(char * data) {
79 return get_config_entry(data, "global", "server");
80}
81
82/* Make sure that the keytab is writable before doing anything */
83static int check_perms(const char *keytab)
84{
85 int ret;
86 int fd;
87
88 ret = access(keytab, W_OK2);
89 if (ret == -1) {
90 switch(errno(*__errno_location ())) {
91 case EACCES13:
92 fprintf(stderrstderr,
93 _("No write permissions on keytab file '%s'\n")gettext("No write permissions on keytab file '%s'\n"),
94 keytab);
95 break;
96 case ENOENT2:
97 /* file doesn't exist, lets touch it and see if writable */
98 fd = open(keytab, O_WRONLY01 | O_CREAT0100, 0600);
99 if (fd != -1) {
100 close(fd);
101 unlink(keytab);
102 return 0;
103 }
104 fprintf(stderrstderr,
105 _("No write permissions on keytab file '%s'\n")gettext("No write permissions on keytab file '%s'\n"),
106 keytab);
107 break;
108 default:
109 fprintf(stderrstderr,
110 _("access() on %1$s failed: errno = %2$d\n")gettext("access() on %1$s failed: errno = %2$d\n"),
111 keytab, errno(*__errno_location ()));
112 break;
113 }
114 return 1;
115 }
116
117 return 0;
118}
119
120/*
121 * There is no API in xmlrpc-c to set arbitrary headers but we can fake it
122 * by using a specially-crafted User-Agent string.
123 *
124 * The caller is responsible for freeing the return value.
125 */
126char *
127set_user_agent(const char *ipaserver) {
128 int ret;
129 char *user_agent = NULL((void*)0);
130
131 ret = asprintf(&user_agent, "%s/%s\r\nReferer: https://%s/ipa/xml\r\nX-Original-User-Agent:", NAME"ipa-join", VERSION"4.9.0.dev202006050815+gitd20cda218", ipaserver);
132 if (ret == -1) {
133 fprintf(stderrstderr, _("Out of memory!")gettext("Out of memory!"));
134 return NULL((void*)0);
135 }
136 return user_agent;
137}
138
139/*
140 * Make an XML-RPC call to methodName. This uses the curl client to make
141 * a connection over SSL using the CA cert that should have been installed
142 * by ipa-client-install.
143 */
144static void
145callRPC(char * user_agent,
146 xmlrpc_env * const envP,
147 xmlrpc_server_info * const serverInfoP,
148 const char * const methodName,
149 xmlrpc_value * const paramArrayP,
150 xmlrpc_value ** const resultPP) {
151
152 struct xmlrpc_clientparms clientparms;
153 struct xmlrpc_curl_xportparms * curlXportParmsP = NULL((void*)0);
154 xmlrpc_client * clientP = NULL((void*)0);
155
156 memset(&clientparms, 0, sizeof(clientparms));
157
158 XMLRPC_ASSERT(xmlrpc_value_type(paramArrayP) == XMLRPC_TYPE_ARRAY)do if (!(xmlrpc_value_type(paramArrayP) == XMLRPC_TYPE_ARRAY)
) xmlrpc_assertion_failed("ipa-join.c", 158); while (0)
;
159
160 curlXportParmsP = malloc(sizeof(*curlXportParmsP));
161 if (curlXportParmsP == NULL((void*)0)) {
162 xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR(-500), _("Out of memory!")gettext("Out of memory!"));
163 return;
164 }
165 memset(curlXportParmsP, 0, sizeof(*curlXportParmsP));
166
167 /* Have curl do SSL certificate validation */
168 curlXportParmsP->no_ssl_verifypeer = 0;
169 curlXportParmsP->no_ssl_verifyhost = 0;
170 curlXportParmsP->cainfo = "/etc/ipa/ca.crt";
171 curlXportParmsP->user_agent = user_agent;
172 /* Enable GSSAPI credentials delegation */
173 curlXportParmsP->gssapi_delegation = 1;
174
175 clientparms.transport = "curl";
176 clientparms.transportparmsP = (struct xmlrpc_xportparms *)
177 curlXportParmsP;
178 clientparms.transportparm_size = XMLRPC_CXPSIZE(gssapi_delegation)(((size_t)(char*)&((struct xmlrpc_curl_xportparms *)0)->
gssapi_delegation) + sizeof(((struct xmlrpc_curl_xportparms *
)0)->gssapi_delegation))
;
179 xmlrpc_client_create(envP, XMLRPC_CLIENT_NO_FLAGS(0), NAME"ipa-join", VERSION"4.9.0.dev202006050815+gitd20cda218",
180 &clientparms, sizeof(clientparms),
181 &clientP);
182
183 /* Set up kerberos negotiate authentication in curl. */
184 xmlrpc_server_info_set_user(envP, serverInfoP, ":", "");
185 xmlrpc_server_info_allow_auth_negotiate(envP, serverInfoP);
186
187 /* Perform the XML-RPC call */
188 if (!envP->fault_occurred) {
189 xmlrpc_client_call2(envP, clientP, serverInfoP, methodName, paramArrayP, resultPP);
190 }
191
192 /* Cleanup */
193 xmlrpc_server_info_free(serverInfoP);
194 xmlrpc_client_destroy(clientP);
195 free((void*)clientparms.transportparmsP);
196}
197
198/* The caller is responsible for unbinding the connection if ld is not NULL */
199static LDAP *
200connect_ldap(const char *hostname, const char *binddn, const char *bindpw,
201 int *ret) {
202 LDAP *ld = NULL((void*)0);
203 int ldapdebug = 2;
204 char *uri = NULL((void*)0);
205 struct berval bindpw_bv;
206
207 *ret = ldap_set_option(NULL((void*)0), LDAP_OPT_DEBUG_LEVEL0x5001, &ldapdebug);
208 if (*ret != LDAP_OPT_SUCCESS0) {
209 goto fail;
210 }
211
212 *ret = asprintf(&uri, "ldaps://%s:636", hostname);
213 if (*ret == -1) {
214 fprintf(stderrstderr, _("Out of memory!")gettext("Out of memory!"));
215 *ret = LDAP_NO_MEMORY(-10);
216 goto fail;
217 }
218
219 *ret = ipa_ldap_init(&ld, uri);
220 if (*ret != LDAP_SUCCESS0x00) {
221 goto fail;
222 }
223 *ret = ipa_tls_ssl_init(ld, uri, DEFAULT_CA_CERT_FILE"/etc/ipa/ca.crt");
224 if (*ret != LDAP_SUCCESS0x00) {
225 fprintf(stderrstderr, _("Unable to enable SSL in LDAP\n")gettext("Unable to enable SSL in LDAP\n"));
226 goto fail;
227 }
228 free(uri);
229 uri = NULL((void*)0);
230
231 if (bindpw) {
232 bindpw_bv.bv_val = discard_const(bindpw)((void *)((uintptr_t)(bindpw)));
233 bindpw_bv.bv_len = strlen(bindpw);
234 } else {
235 bindpw_bv.bv_val = NULL((void*)0);
236 bindpw_bv.bv_len = 0;
237 }
238
239 *ret = ldap_sasl_bind_s(ld, binddn, LDAP_SASL_SIMPLE((char*)0), &bindpw_bv,
240 NULL((void*)0), NULL((void*)0), NULL((void*)0));
241
242 if (*ret != LDAP_SUCCESS0x00) {
243 ipa_ldap_error(ld, *ret, _("SASL Bind failed\n")gettext("SASL Bind failed\n"));
244 goto fail;
245 }
246
247 return ld;
248
249fail:
250 if (ld != NULL((void*)0)) {
251 ldap_unbind_ext(ld, NULL((void*)0), NULL((void*)0));
252 }
253 if (uri != NULL((void*)0)) {
254 free(uri);
255 }
256 return NULL((void*)0);
257}
258
259/*
260 * Given a list of naming contexts check each one to see if it has
261 * an IPA v2 server in it. The first one we find wins.
262 */
263static int
264check_ipa_server(LDAP *ld, char **ldap_base, struct berval **vals)
265{
266 struct berval **infovals;
267 LDAPMessage *entry, *res = NULL((void*)0);
268 char *info_attrs[] = {"info", NULL((void*)0)};
269 int i, ret = 0;
270
271 for (i = 0; !*ldap_base && vals[i]; i++) {
272 ret = ldap_search_ext_s(ld, vals[i]->bv_val,
273 LDAP_SCOPE_BASE((ber_int_t) 0x0000), "(info=IPA*)", info_attrs,
274 0, NULL((void*)0), NULL((void*)0), NULL((void*)0), 0, &res);
275
276 if (ret != LDAP_SUCCESS0x00) {
277 break;
278 }
279
280 entry = ldap_first_entry(ld, res);
281 infovals = ldap_get_values_len(ld, entry, info_attrs[0]);
282 if (!strcmp(infovals[0]->bv_val, "IPA V2.0"))
283 *ldap_base = strdup(vals[i]->bv_val);
284 ldap_msgfree(res);
285 res = NULL((void*)0);
286 }
287
288 return ret;
289}
290
291/*
292 * Determine the baseDN of the remote server. Look first for a
293 * defaultNamingContext, otherwise fall back to reviewing each
294 * namingContext.
295 */
296static int
297get_root_dn(const char *ipaserver, char **ldap_base)
298{
299 LDAP *ld = NULL((void*)0);
300 char *root_attrs[] = {"namingContexts", "defaultNamingContext", NULL((void*)0)};
301 LDAPMessage *entry, *res = NULL((void*)0);
302 struct berval **ncvals;
303 struct berval **defvals;
304 int ret, rval = 0;
305
306 ld = connect_ldap(ipaserver, NULL((void*)0), NULL((void*)0), &ret);
307 if (!ld) {
308 rval = 14;
309 goto done;
310 }
311
312 ret = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE((ber_int_t) 0x0000),
313 "objectclass=*", root_attrs, 0,
314 NULL((void*)0), NULL((void*)0), NULL((void*)0), 0, &res);
315
316 if (ret != LDAP_SUCCESS0x00) {
317 fprintf(stderrstderr, _("Search for %1$s on rootdse failed with error %2$d\n")gettext("Search for %1$s on rootdse failed with error %2$d\n"
)
,
318 root_attrs[0], ret);
319 rval = 14;
320 goto done;
321 }
322
323 *ldap_base = NULL((void*)0);
324
325 entry = ldap_first_entry(ld, res);
326
327 defvals = ldap_get_values_len(ld, entry, root_attrs[1]);
328 if (defvals) {
329 ret = check_ipa_server(ld, ldap_base, defvals);
330 }
331 ldap_value_free_len(defvals);
332
333 /* loop through to find the IPA context */
334 if (ret == LDAP_SUCCESS0x00 && !*ldap_base) {
335 ncvals = ldap_get_values_len(ld, entry, root_attrs[0]);
336 if (!ncvals) {
337 fprintf(stderrstderr, _("No values for %s")gettext("No values for %s"), root_attrs[0]);
338 rval = 14;
339 ldap_value_free_len(ncvals);
340 goto done;
341 }
342 ret = check_ipa_server(ld, ldap_base, ncvals);
343 ldap_value_free_len(ncvals);
344 }
345
346 if (ret != LDAP_SUCCESS0x00) {
347 fprintf(stderrstderr, _("Search for IPA namingContext failed with error %d\n")gettext("Search for IPA namingContext failed with error %d\n"
)
, ret);
348 rval = 14;
349 goto done;
350 }
351
352 if (!*ldap_base) {
353 fprintf(stderrstderr, _("IPA namingContext not found\n")gettext("IPA namingContext not found\n"));
354 rval = 14;
355 goto done;
356 }
357
358
359done:
360 if (res) ldap_msgfree(res);
361 if (ld != NULL((void*)0)) {
362 ldap_unbind_ext(ld, NULL((void*)0), NULL((void*)0));
363 }
364
365 return rval;
366}
367
368/* Join a host to the current IPA realm.
369 *
370 * There are several scenarios for this:
371 * 1. You are an IPA admin user with fullrights to add hosts and generate
372 * keytabs.
373 * 2. You are an IPA admin user with rights to generate keytabs but not
374 * write hosts.
375 * 3. You are a regular IPA user with a password that can be used to
376 * generate the host keytab.
377 *
378 * If a password is presented it will be used regardless of the rights of
379 * the user.
380 */
381
382/* If we only have a bindpw then try to join in a bit of a degraded mode.
383 * This is going to duplicate some of the server-side code to determine
384 * the state of the entry.
385 */
386static int
387join_ldap(const char *ipaserver, char *hostname, char ** binddn, const char *bindpw, const char *basedn, const char **princ, int quiet)
388{
389 LDAP *ld;
390 int rval = 0;
391 char *oidresult = NULL((void*)0);
392 struct berval valrequest;
393 struct berval *valresult = NULL((void*)0);
394 int rc, ret;
395 char *ldap_base = NULL((void*)0);
396
397 *binddn = NULL((void*)0);
398 *princ = NULL((void*)0);
399
400 if (NULL((void*)0) != basedn) {
401 ldap_base = strdup(basedn);
402 if (!ldap_base) {
403 if (!quiet)
404 fprintf(stderrstderr, _("Out of memory!\n")gettext("Out of memory!\n"));
405 rval = 3;
406 goto done;
407 }
408 } else {
409 if (get_root_dn(ipaserver, &ldap_base) != 0) {
410 if (!quiet)
411 fprintf(stderrstderr, _("Unable to determine root DN of %s\n")gettext("Unable to determine root DN of %s\n"),
412 ipaserver);
413 rval = 14;
414 goto done;
415 }
416 }
417
418 ret = asprintf(binddn, "fqdn=%s,cn=computers,cn=accounts,%s", hostname, ldap_base);
419 if (ret == -1)
420 {
421 if (!quiet)
422 fprintf(stderrstderr, _("Out of memory!\n")gettext("Out of memory!\n"));
423 rval = 3;
424 goto done;
425 }
426 ld = connect_ldap(ipaserver, *binddn, bindpw, &ret);
427 if (!ld) {
428 if (quiet)
429 goto done;
430
431 switch(ret) {
432 case LDAP_NO_MEMORY(-10):
433 rval = 3;
434 break;
435 case LDAP_INVALID_CREDENTIALS0x31: /* incorrect password */
436 case LDAP_INAPPROPRIATE_AUTH0x30: /* no password set */
437 rval = 15;
438 break;
439 default: /* LDAP connection error catch-all */
440 rval = 14;
441 break;
442 }
443 goto done;
444 }
445
446 valrequest.bv_val = (char *)hostname;
447 valrequest.bv_len = strlen(hostname);
448
449 if ((rc = ldap_extended_operation_s(ld, JOIN_OID"2.16.840.1.113730.3.8.10.3", &valrequest, NULL((void*)0), NULL((void*)0), &oidresult, &valresult)) != LDAP_SUCCESS0x00) {
450 char *s = NULL((void*)0);
451#ifdef LDAP_OPT_DIAGNOSTIC_MESSAGE0x0032
452 ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE0x0032, &s);
453#else
454 ldap_get_option(ld, LDAP_OPT_ERROR_STRING0x0032, &s);
455#endif
456 if (!quiet)
457 fprintf(stderrstderr, _("Enrollment failed. %s\n")gettext("Enrollment failed. %s\n"), s);
458 if (debug) {
459 fprintf(stderrstderr, "ldap_extended_operation_s failed: %s",
460 ldap_err2string(rc));
461 }
462 rval = 13;
463 goto ldap_done;
464 }
465
466 /* Get the value from the result returned by the server. */
467 *princ = strdup(valresult->bv_val);
468
469ldap_done:
470 if (ld != NULL((void*)0)) {
471 ldap_unbind_ext(ld, NULL((void*)0), NULL((void*)0));
472 }
473
474done:
475 free(ldap_base);
476 if (valresult) ber_bvfree(valresult);
477 if (oidresult) free(oidresult);
478 return rval;
479}
480
481static int
482join_krb5(const char *ipaserver, char *hostname, char **hostdn, const char **princ, int force, int quiet) {
483 xmlrpc_env env;
484 xmlrpc_value * argArrayP = NULL((void*)0);
485 xmlrpc_value * paramArrayP = NULL((void*)0);
486 xmlrpc_value * paramP = NULL((void*)0);
487 xmlrpc_value * optionsP = NULL((void*)0);
488 xmlrpc_value * resultP = NULL((void*)0);
489 xmlrpc_value * structP = NULL((void*)0);
490 xmlrpc_server_info * serverInfoP = NULL((void*)0);
491 struct utsname uinfo;
492 xmlrpc_value *princP = NULL((void*)0);
493 xmlrpc_value *krblastpwdchangeP = NULL((void*)0);
494 xmlrpc_value *hostdnP = NULL((void*)0);
495 const char *krblastpwdchange = NULL((void*)0);
496 char * url = NULL((void*)0);
497 char * user_agent = NULL((void*)0);
498 int rval = 0;
499 int ret;
500
501 *hostdn = NULL((void*)0);
502 *princ = NULL((void*)0);
503
504 /* Start up our XML-RPC client library. */
505 xmlrpc_client_init(XMLRPC_CLIENT_NO_FLAGS(0), NAME"ipa-join", VERSION"4.9.0.dev202006050815+gitd20cda218");
506
507 uname(&uinfo);
508
509 xmlrpc_env_init(&env);
510
511 xmlrpc_client_setup_global_const(&env);
512
513#if 1
514 ret = asprintf(&url, "https://%s:443/ipa/xml", ipaserver);
515#else
516 ret = asprintf(&url, "http://%s:8888/", ipaserver);
517#endif
518 if (ret == -1)
519 {
520 if (!quiet)
521 fprintf(stderrstderr, _("Out of memory!\n")gettext("Out of memory!\n"));
522 rval = 3;
523 goto cleanup;
524 }
525
526 serverInfoP = xmlrpc_server_info_new(&env, url);
527
528 argArrayP = xmlrpc_array_new(&env);
529 paramArrayP = xmlrpc_array_new(&env);
530
531 if (hostname == NULL((void*)0))
532 paramP = xmlrpc_string_new(&env, uinfo.nodename);
533 else
534 paramP = xmlrpc_string_new(&env, hostname);
535 xmlrpc_array_append_item(&env, argArrayP, paramP);
536#ifdef REALM
537 if (!quiet)
538 printf("Joining %s to IPA realm %s\n", uinfo.nodename, iparealm);
539#endif
540 xmlrpc_array_append_item(&env, paramArrayP, argArrayP);
541 xmlrpc_DECREF(paramP);
542
543 optionsP = xmlrpc_build_value(&env, "{s:s,s:s}",
544 "nsosversion", uinfo.release,
545 "nshardwareplatform", uinfo.machine);
546 xmlrpc_array_append_item(&env, paramArrayP, optionsP);
547 xmlrpc_DECREF(optionsP);
548
549 if ((user_agent = set_user_agent(ipaserver)) == NULL((void*)0)) {
550 rval = 3;
551 goto cleanup;
552 }
553 callRPC(user_agent, &env, serverInfoP, "join", paramArrayP, &resultP);
554 if (handle_fault(&env)) {
555 rval = 17;
556 goto cleanup_xmlrpc;
557 }
558
559 /* Return value is the form of an array. The first value is the
560 * DN, the second a struct of attribute values
561 */
562 xmlrpc_array_read_item(&env, resultP, 0, &hostdnP);
563 xmlrpc_read_string(&env, hostdnP, (const char **)hostdn);
564 xmlrpc_DECREF(hostdnP);
565 xmlrpc_array_read_item(&env, resultP, 1, &structP);
566
567 xmlrpc_struct_find_value(&env, structP, "krbprincipalname", &princP);
568 if (princP) {
569 xmlrpc_value * singleprincP = NULL((void*)0);
570
571 /* FIXME: all values are returned as lists currently. Once this is
572 * fixed we can read the string directly.
573 */
574 xmlrpc_array_read_item(&env, princP, 0, &singleprincP);
575 xmlrpc_read_string(&env, singleprincP, &*princ);
576 xmlrpc_DECREF(princP);
577 xmlrpc_DECREF(singleprincP);
578 } else {
579 if (!quiet)
580 fprintf(stderrstderr, _("principal not found in XML-RPC response\n")gettext("principal not found in XML-RPC response\n"));
581 rval = 12;
582 goto cleanup;
583 }
584 xmlrpc_struct_find_value(&env, structP, "krblastpwdchange", &krblastpwdchangeP);
585 if (krblastpwdchangeP && !force) {
586 xmlrpc_value * singleprincP = NULL((void*)0);
587
588 /* FIXME: all values are returned as lists currently. Once this is
589 * fixed we can read the string directly.
590 */
591 xmlrpc_array_read_item(&env, krblastpwdchangeP, 0, &singleprincP);
592 xmlrpc_read_string(&env, singleprincP, &krblastpwdchange);
593 xmlrpc_DECREF(krblastpwdchangeP);
594 if (!quiet)
595 fprintf(stderrstderr, _("Host is already joined.\n")gettext("Host is already joined.\n"));
596 rval = 13;
597 goto cleanup;
598 }
599
600cleanup:
601 if (argArrayP) xmlrpc_DECREF(argArrayP);
602 if (paramArrayP) xmlrpc_DECREF(paramArrayP);
603 if (resultP) xmlrpc_DECREF(resultP);
604
605cleanup_xmlrpc:
606 free(user_agent);
607 free(url);
608 free((char *)krblastpwdchange);
609 xmlrpc_env_clean(&env);
610 xmlrpc_client_cleanup();
611
612 return rval;
613}
614
615static int
616unenroll_host(const char *server, const char *hostname, const char *ktname, int quiet)
617{
618 int rval = 0;
619 int ret;
620 char *ipaserver = NULL((void*)0);
621 char *host = NULL((void*)0);
622 struct utsname uinfo;
623 char *principal = NULL((void*)0);
624 char *realm = NULL((void*)0);
625
626 krb5_context krbctx = NULL((void*)0);
627 krb5_keytab keytab = NULL((void*)0);
628 krb5_ccache ccache = NULL((void*)0);
629 krb5_principal princ = NULL((void*)0);
630 krb5_error_code krberr;
631 krb5_creds creds;
632 krb5_get_init_creds_opt gicopts;
633 char tgs[LINE_MAX2048];
634
635 xmlrpc_env env;
636 xmlrpc_value * argArrayP = NULL((void*)0);
637 xmlrpc_value * paramArrayP = NULL((void*)0);
638 xmlrpc_value * paramP = NULL((void*)0);
639 xmlrpc_value * resultP = NULL((void*)0);
640 xmlrpc_server_info * serverInfoP = NULL((void*)0);
641 xmlrpc_value *princP = NULL((void*)0);
642 char * url = NULL((void*)0);
643 char * user_agent = NULL((void*)0);
644
645 /* Start up our XML-RPC client library. */
646 xmlrpc_client_init(XMLRPC_CLIENT_NO_FLAGS(0), NAME"ipa-join", VERSION"4.9.0.dev202006050815+gitd20cda218");
647
648 xmlrpc_env_init(&env);
649
650 xmlrpc_client_setup_global_const(&env);
651
652 if (server) {
653 ipaserver = strdup(server);
654 } else {
655 char * conf_data = read_config_file(IPA_CONFIG"/etc/ipa/default.conf");
656 if ((ipaserver = getIPAserver(conf_data)) == NULL((void*)0)) {
657 if (!quiet)
658 fprintf(stderrstderr, _("Unable to determine IPA server from %s\n")gettext("Unable to determine IPA server from %s\n"),
659 IPA_CONFIG"/etc/ipa/default.conf");
660 exit(1);
661 }
662 free(conf_data);
663 }
664
665 if (NULL((void*)0) == hostname) {
666 uname(&uinfo);
667 host = strdup(uinfo.nodename);
668 } else {
669 host = strdup(hostname);
670 }
671
672 if (NULL((void*)0) == host) {
673 rval = 3;
674 goto cleanup;
675 }
676
677 if (NULL((void*)0) == strstr(host, ".")) {
678 if (!quiet)
679 fprintf(stderrstderr, _("The hostname must be fully-qualified: %s\n")gettext("The hostname must be fully-qualified: %s\n"),
680 host);
681 rval = 16;
682 goto cleanup;
683 }
684
685 krberr = krb5_init_context(&krbctx);
686 if (krberr) {
687 if (!quiet)
688 fprintf(stderrstderr, _("Unable to join host: "gettext("Unable to join host: " "Kerberos context initialization failed\n"
)
689 "Kerberos context initialization failed\n")gettext("Unable to join host: " "Kerberos context initialization failed\n"
)
);
690 rval = 1;
691 goto cleanup;
692 }
693 krberr = krb5_kt_resolve(krbctx, ktname, &keytab);
694 if (krberr != 0) {
695 if (!quiet)
696 fprintf(stderrstderr, _("Error resolving keytab: %s.\n")gettext("Error resolving keytab: %s.\n"),
697 error_message(krberr));
698 rval = 7;
699 goto cleanup;
700 }
701
702 krberr = krb5_get_default_realm(krbctx, &realm);
703 if (krberr != 0) {
704 if (!quiet)
705 fprintf(stderrstderr, _("Error getting default Kerberos realm: %s.\n")gettext("Error getting default Kerberos realm: %s.\n"),
706 error_message(krberr));
707 rval = 21;
708 goto cleanup;
709 }
710
711 ret = asprintf(&principal, "host/%s@%s", host, realm);
712 if (ret == -1)
713 {
714 if (!quiet)
715 fprintf(stderrstderr, _("Out of memory!\n")gettext("Out of memory!\n"));
716 rval = 3;
717 goto cleanup;
718 }
719
720 krberr = krb5_parse_name(krbctx, principal, &princ);
721 if (krberr != 0) {
722 if (!quiet)
723 fprintf(stderrstderr, _("Error parsing \"%1$s\": %2$s.\n")gettext("Error parsing \"%1$s\": %2$s.\n"),
724 principal, error_message(krberr));
725 rval = 4;
726 goto cleanup;
727 }
728 strcpy(tgs, KRB5_TGS_NAME"krbtgt");
729 snprintf(tgs + strlen(tgs), sizeof(tgs) - strlen(tgs), "/%.*s",
730 (krb5_princ_realm(krbctx, princ)(&(princ)->realm))->length,
731 (krb5_princ_realm(krbctx, princ)(&(princ)->realm))->data);
732 snprintf(tgs + strlen(tgs), sizeof(tgs) - strlen(tgs), "@%.*s",
733 (krb5_princ_realm(krbctx, princ)(&(princ)->realm))->length,
734 (krb5_princ_realm(krbctx, princ)(&(princ)->realm))->data);
735 memset(&creds, 0, sizeof(creds));
736 krb5_get_init_creds_opt_init(&gicopts);
737 krb5_get_init_creds_opt_set_forwardable(&gicopts, 1);
738 krberr = krb5_get_init_creds_keytab(krbctx, &creds, princ, keytab,
739 0, tgs, &gicopts);
740 if (krberr != 0) {
741 if (!quiet)
742 fprintf(stderrstderr, _("Error obtaining initial credentials: %s.\n")gettext("Error obtaining initial credentials: %s.\n"),
743 error_message(krberr));
744 rval = 19;
745 goto cleanup;
746 }
747
748 krberr = krb5_cc_resolve(krbctx, "MEMORY:ipa-join", &ccache);
749 if (krberr == 0) {
750 krberr = krb5_cc_initialize(krbctx, ccache, creds.client);
Value stored to 'krberr' is never read
751 } else {
752 if (!quiet)
753 fprintf(stderrstderr,
754 _("Unable to generate Kerberos Credential Cache\n")gettext("Unable to generate Kerberos Credential Cache\n"));
755 rval = 19;
756 goto cleanup;
757 }
758 krberr = krb5_cc_store_cred(krbctx, ccache, &creds);
759 if (krberr != 0) {
760 if (!quiet)
761 fprintf(stderrstderr,
762 _("Error storing creds in credential cache: %s.\n")gettext("Error storing creds in credential cache: %s.\n"),
763 error_message(krberr));
764 rval = 19;
765 goto cleanup;
766 }
767 krb5_cc_close(krbctx, ccache);
768 ccache = NULL((void*)0);
769 putenv("KRB5CCNAME=MEMORY:ipa-join");
770
771#if 1
772 ret = asprintf(&url, "https://%s:443/ipa/xml", ipaserver);
773#else
774 ret = asprintf(&url, "http://%s:8888/", ipaserver);
775#endif
776 if (ret == -1)
777 {
778 if (!quiet)
779 fprintf(stderrstderr, _("Out of memory!\n")gettext("Out of memory!\n"));
780 rval = 3;
781 goto cleanup;
782 }
783 serverInfoP = xmlrpc_server_info_new(&env, url);
784
785 argArrayP = xmlrpc_array_new(&env);
786 paramArrayP = xmlrpc_array_new(&env);
787
788 paramP = xmlrpc_string_new(&env, host);
789 xmlrpc_array_append_item(&env, argArrayP, paramP);
790 xmlrpc_array_append_item(&env, paramArrayP, argArrayP);
791 xmlrpc_DECREF(paramP);
792
793 if ((user_agent = set_user_agent(ipaserver)) == NULL((void*)0)) {
794 rval = 3;
795 goto cleanup;
796 }
797 callRPC(user_agent, &env, serverInfoP, "host_disable", paramArrayP, &resultP);
798 if (handle_fault(&env)) {
799 rval = 17;
800 goto cleanup;
801 }
802
803 xmlrpc_struct_find_value(&env, resultP, "result", &princP);
804 if (princP) {
805 xmlrpc_bool result;
806
807 xmlrpc_read_bool(&env, princP, &result);
808 if (result == 1) {
809 if (!quiet)
810 fprintf(stderrstderr, _("Unenrollment successful.\n")gettext("Unenrollment successful.\n"));
811 } else {
812 if (!quiet)
813 fprintf(stderrstderr, _("Unenrollment failed.\n")gettext("Unenrollment failed.\n"));
814 }
815
816 xmlrpc_DECREF(princP);
817 } else {
818 fprintf(stderrstderr, _("result not found in XML-RPC response\n")gettext("result not found in XML-RPC response\n"));
819 rval = 20;
820 goto cleanup;
821 }
822
823cleanup:
824
825 free(user_agent);
826 if (keytab) krb5_kt_close(krbctx, keytab);
827 free(host);
828 free((char *)principal);
829 free((char *)ipaserver);
830 if (princ) krb5_free_principal(krbctx, princ);
831 if (ccache) krb5_cc_close(krbctx, ccache);
832 if (krbctx) krb5_free_context(krbctx);
833
834 free(url);
835 xmlrpc_env_clean(&env);
836 xmlrpc_client_cleanup();
837
838 return rval;
839}
840
841
842static int
843join(const char *server, const char *hostname, const char *bindpw, const char *basedn, const char *keytab, int force, int quiet)
844{
845 int rval = 0;
846 pid_t childpid = 0;
847 int status = 0;
848 char *ipaserver = NULL((void*)0);
849 char *iparealm = NULL((void*)0);
850 char * host = NULL((void*)0);
851 const char * princ = NULL((void*)0);
852 char * hostdn = NULL((void*)0);
853 struct utsname uinfo;
854
855 krb5_context krbctx = NULL((void*)0);
856 krb5_ccache ccache = NULL((void*)0);
857 krb5_principal uprinc = NULL((void*)0);
858 krb5_error_code krberr;
859
860 if (server) {
861 ipaserver = strdup(server);
862 } else {
863 char * conf_data = read_config_file(IPA_CONFIG"/etc/ipa/default.conf");
864 if ((ipaserver = getIPAserver(conf_data)) == NULL((void*)0)) {
865 fprintf(stderrstderr, _("Unable to determine IPA server from %s\n")gettext("Unable to determine IPA server from %s\n"),
866 IPA_CONFIG"/etc/ipa/default.conf");
867 exit(1);
868 }
869 free(conf_data);
870 }
871
872 if (NULL((void*)0) == hostname) {
873 uname(&uinfo);
874 host = strdup(uinfo.nodename);
875 } else {
876 host = strdup(hostname);
877 }
878
879 if (NULL((void*)0) == strstr(host, ".")) {
880 fprintf(stderrstderr, _("The hostname must be fully-qualified: %s\n")gettext("The hostname must be fully-qualified: %s\n"), host);
881 rval = 16;
882 goto cleanup;
883 }
884
885 if ((!strcmp(host, "localhost")) || (!strcmp(host, "localhost.localdomain"))){
886 fprintf(stderrstderr, _("The hostname must not be: %s\n")gettext("The hostname must not be: %s\n"), host);
887 rval = 16;
888 goto cleanup;
889 }
890
891 if (bindpw)
892 rval = join_ldap(ipaserver, host, &hostdn, bindpw, basedn, &princ, quiet);
893 else {
894 krberr = krb5_init_context(&krbctx);
895 if (krberr) {
896 fprintf(stderrstderr, _("Unable to join host: "gettext("Unable to join host: " "Kerberos context initialization failed\n"
)
897 "Kerberos context initialization failed\n")gettext("Unable to join host: " "Kerberos context initialization failed\n"
)
);
898 rval = 1;
899 goto cleanup;
900 }
901 krberr = krb5_cc_default(krbctx, &ccache);
902 if (krberr) {
903 fprintf(stderrstderr, _("Unable to join host:"gettext("Unable to join host:" " Kerberos Credential Cache not found\n"
)
904 " Kerberos Credential Cache not found\n")gettext("Unable to join host:" " Kerberos Credential Cache not found\n"
)
);
905 rval = 5;
906 goto cleanup;
907 }
908
909 krberr = krb5_cc_get_principal(krbctx, ccache, &uprinc);
910 if (krberr) {
911 fprintf(stderrstderr, _("Unable to join host: Kerberos User Principal "gettext("Unable to join host: Kerberos User Principal " "not found and host password not provided.\n"
)
912 "not found and host password not provided.\n")gettext("Unable to join host: Kerberos User Principal " "not found and host password not provided.\n"
)
);
913 rval = 6;
914 goto cleanup;
915 }
916 rval = join_krb5(ipaserver, host, &hostdn, &princ, force,
917 quiet);
918 }
919
920 if (rval) goto cleanup;
921
922 /* Fork off and let ipa-getkeytab generate the keytab for us */
923 childpid = fork();
924
925 if (childpid < 0) {
926 fprintf(stderrstderr, _("fork() failed\n")gettext("fork() failed\n"));
927 rval = 1;
928 goto cleanup;
929 }
930
931 if (childpid == 0) {
932 char *argv[12];
933 char *path = "/usr/sbin/ipa-getkeytab";
934 int arg = 0;
935 int err;
936
937 argv[arg++] = path;
938 argv[arg++] = "-s";
939 argv[arg++] = ipaserver;
940 argv[arg++] = "-p";
941 argv[arg++] = (char *)princ;
942 argv[arg++] = "-k";
943 argv[arg++] = (char *)keytab;
944 if (bindpw) {
945 argv[arg++] = "-D";
946 argv[arg++] = (char *)hostdn;
947 argv[arg++] = "-w";
948 argv[arg++] = (char *)bindpw;
949 }
950 argv[arg++] = NULL((void*)0);
951 err = execv(path, argv);
952 if (err == -1) {
953 switch(errno(*__errno_location ())) {
954 case ENOENT2:
955 fprintf(stderrstderr, _("ipa-getkeytab not found\n")gettext("ipa-getkeytab not found\n"));
956 break;
957 case EACCES13:
958 fprintf(stderrstderr, _("ipa-getkeytab has bad permissions?\n")gettext("ipa-getkeytab has bad permissions?\n"));
959 break;
960 default:
961 fprintf(stderrstderr, _("executing ipa-getkeytab failed, "gettext("executing ipa-getkeytab failed, " "errno %d\n")
962 "errno %d\n")gettext("executing ipa-getkeytab failed, " "errno %d\n"), errno(*__errno_location ()));
963 break;
964 }
965 }
966 } else {
967 wait(&status);
968 }
969
970 if WIFEXITED(status)(((status) & 0x7f) == 0) {
971 rval = WEXITSTATUS(status)(((status) & 0xff00) >> 8);
972 if (rval != 0) {
973 fprintf(stderrstderr, _("child exited with %d\n")gettext("child exited with %d\n"), rval);
974 }
975 }
976
977cleanup:
978 free((char *)princ);
979 free(host);
980
981 if (bindpw)
982 ldap_memfree((void *)hostdn);
983 else
984 free((char *)hostdn);
985
986 free((char *)ipaserver);
987 free((char *)iparealm);
988 if (uprinc) krb5_free_principal(krbctx, uprinc);
989 if (ccache) krb5_cc_close(krbctx, ccache);
990 if (krbctx) krb5_free_context(krbctx);
991
992 return rval;
993}
994
995/*
996 * Note, an intention with return values is so that this is compatible with
997 * ipa-getkeytab. This is so based on the return value you can distinguish
998 * between errors common between the two (no kerbeors ccache) and those
999 * unique (host already added).
1000 */
1001int
1002main(int argc, const char **argv) {
1003 static const char *hostname = NULL((void*)0);
1004 static const char *server = NULL((void*)0);
1005 static const char *keytab = NULL((void*)0);
1006 static const char *bindpw = NULL((void*)0);
1007 static const char *basedn = NULL((void*)0);
1008 int quiet = 0;
1009 int unenroll = 0;
1010 int force = 0;
1011 struct poptOption options[] = {
1012 { "debug", 'd', POPT_ARG_NONE0U, &debug, 0,
1013 _("Print the raw XML-RPC output in GSSAPI mode")gettext("Print the raw XML-RPC output in GSSAPI mode"), NULL((void*)0) },
1014 { "quiet", 'q', POPT_ARG_NONE0U, &quiet, 0,
1015 _("Quiet mode. Only errors are displayed.")gettext("Quiet mode. Only errors are displayed."), NULL((void*)0) },
1016 { "unenroll", 'u', POPT_ARG_NONE0U, &unenroll, 0,
1017 _("Unenroll this host from IPA server")gettext("Unenroll this host from IPA server"), NULL((void*)0) },
1018 { "hostname", 'h', POPT_ARG_STRING1U, &hostname, 0,
1019 _("Hostname of this server")gettext("Hostname of this server"), _("hostname")gettext("hostname") },
1020 { "server", 's', POPT_ARG_STRING1U, &server, 0,
1021 _("IPA Server to use")gettext("IPA Server to use"), _("hostname")gettext("hostname") },
1022 { "keytab", 'k', POPT_ARG_STRING1U, &keytab, 0,
1023 _("Specifies where to store keytab information.")gettext("Specifies where to store keytab information."), _("filename")gettext("filename") },
1024 { "force", 'f', POPT_ARG_NONE0U, &force, 0,
1025 _("Force the host join. Rejoin even if already joined.")gettext("Force the host join. Rejoin even if already joined."
)
, NULL((void*)0) },
1026 { "bindpw", 'w', POPT_ARG_STRING1U, &bindpw, 0,
1027 _("LDAP password (if not using Kerberos)")gettext("LDAP password (if not using Kerberos)"), _("password")gettext("password") },
1028 { "basedn", 'b', POPT_ARG_STRING1U, &basedn, 0,
1029 _("LDAP basedn")gettext("LDAP basedn"), _("basedn")gettext("basedn") },
1030 POPT_AUTOHELP{ ((void*)0), '\0', 4U, poptHelpOptions, 0, "Help options:", (
(void*)0) },
1031 POPT_TABLEEND{ ((void*)0), '\0', 0, ((void*)0), 0, ((void*)0), ((void*)0) }
1032 };
1033 poptContext pc;
1034 int ret;
1035
1036 ret = init_gettext();
1037 if (ret) {
1038 fprintf(stderrstderr, "Failed to load translations\n");
1039 }
1040
1041 pc = poptGetContext("ipa-join", argc, (const char **)argv, options, 0);
1042 ret = poptGetNextOpt(pc);
1043 if (ret != -1) {
1044 if (!quiet) {
1045 poptPrintUsage(pc, stderrstderr, 0);
1046 }
1047 exit(2);
1048 }
1049 poptFreeContext(pc);
1050 if (debug)
1051 setenv("XMLRPC_TRACE_XML", "1", 1);
1052
1053
1054 if (!keytab)
1055 keytab = "/etc/krb5.keytab";
1056
1057 if (unenroll) {
1058 ret = unenroll_host(server, hostname, keytab, quiet);
1059 } else {
1060 ret = check_perms(keytab);
1061 if (ret == 0)
1062 ret = join(server, hostname, bindpw, basedn, keytab, force, quiet);
1063 }
1064
1065 exit(ret);
1066}