2
/* Kpasswd-LDAP proxy */
4
/* Authors: Simo Sorce <ssorce@redhat.com>
6
* Copyright (C) 2007, 2008 Red Hat
7
* see file 'COPYING' for use and warranty information
9
* This program is free software you can redistribute it and/or modify
10
* it under the terms of the GNU General Public License as published by
11
* the Free Software Foundation, either version 3 of the License, or
12
* (at your option) any later version.
14
* This program is distributed in the hope that it will be useful,
15
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
* GNU General Public License for more details.
19
* You should have received a copy of the GNU General Public License
20
* along with this program. If not, see <http://www.gnu.org/licenses/>.
25
#include <sys/types.h>
26
#include <sys/socket.h>
38
#include <netinet/in.h>
39
#include <arpa/inet.h>
43
#include <sasl/sasl.h>
46
#define DEFAULT_KEYTAB "FILE:/var/kerberos/krb5kdc/kpasswd.keytab"
47
#define TMP_TEMPLATE "/var/cache/ipa/kpasswd/krb5_cc.XXXXXX"
49
/* blacklist entries are released only BLCAKLIST_TIMEOUT seconds
50
* after the children performing the noperation has finished.
51
* this is to avoid races */
53
#define BLACKLIST_TIMEOUT 5
56
struct blacklist *next;
62
static struct blacklist *global_blacklist = NULL;
68
struct sockaddr_storage dest_addr;
69
struct socklist *next;
72
int check_blacklist(char *address)
74
struct blacklist *bl, *prev_bl;
75
time_t now = time(NULL);
77
if (!global_blacklist) {
82
bl = global_blacklist;
84
if (bl->expire && (bl->expire < now)) {
86
prev_bl->next = bl->next;
91
global_blacklist = bl->next;
94
bl = global_blacklist;
99
if (strcmp(address, bl->address) == 0) {
110
int add_blacklist(pid_t pid, char *address)
112
struct blacklist *bl, *gbl;
114
bl = malloc(sizeof(struct blacklist));
120
bl->address = strdup(address);
126
if (!global_blacklist) {
127
global_blacklist = bl;
131
gbl = global_blacklist;
139
int remove_blacklist(pid_t pid)
141
struct blacklist *bl;
143
if (!global_blacklist) {
147
bl = global_blacklist;
149
if (pid == bl->pid) {
150
bl->expire = time(NULL) + BLACKLIST_TIMEOUT;
159
char *srv_pri_name = "kadmin/changepw";
160
char *keytab_name = NULL;
162
static int get_krb5_ticket(char *tmp_file)
165
char *realm_name = NULL;
166
krb5_context context = NULL;
167
krb5_keytab keytab = NULL;
168
krb5_ccache ccache = NULL;
169
krb5_principal kprincpw;
171
krb5_get_init_creds_opt options;
172
krb5_error_code krberr;
175
krberr = krb5_init_context(&context);
177
syslog(LOG_ERR, "Failed to init kerberos context");
181
krberr = krb5_get_default_realm(context, &realm_name);
183
syslog(LOG_ERR, "Failed to get default realm name: %s",
184
krb5_get_error_message(context, krberr));
189
krberr = krb5_build_principal(context, &kprincpw,
190
strlen(realm_name), realm_name,
191
"kadmin", "changepw", NULL);
193
syslog(LOG_ERR, "Unable to build principal: %s",
194
krb5_get_error_message(context, krberr));
199
krberr = krb5_kt_resolve(context, keytab_name, &keytab);
201
syslog(LOG_ERR, "Failed to read keytab file: %s",
202
krb5_get_error_message(context, krberr));
207
ret = asprintf(&ccname, "FILE:%s", tmp_file);
209
syslog(LOG_ERR, "Out of memory!");
213
ret = setenv("KRB5CCNAME", ccname, 1);
215
syslog(LOG_ERR, "Unable to set env. variable KRB5CCNAME!");
219
krberr = krb5_cc_resolve(context, ccname, &ccache);
221
syslog(LOG_ERR, "Failed to set cache name: %s",
222
krb5_get_error_message(context, krberr));
227
memset(&my_creds, 0, sizeof(my_creds));
228
memset(&options, 0, sizeof(options));
230
krb5_get_init_creds_opt_set_address_list(&options, NULL);
231
krb5_get_init_creds_opt_set_forwardable(&options, 0);
232
krb5_get_init_creds_opt_set_proxiable(&options, 0);
233
/* set a very short lifetime, we don't keep the ticket around */
234
krb5_get_init_creds_opt_set_tkt_life(&options, 300);
236
krberr = krb5_get_init_creds_keytab(context, &my_creds, kprincpw,
241
syslog(LOG_ERR, "Failed to init credentials: %s",
242
krb5_get_error_message(context, krberr));
247
krberr = krb5_cc_initialize(context, ccache, kprincpw);
249
syslog(LOG_ERR, "Failed to init ccache: %s",
250
krb5_get_error_message(context, krberr));
255
krberr = krb5_cc_store_cred(context, ccache, &my_creds);
257
syslog(LOG_ERR, "Failed to store creds: %s",
258
krb5_get_error_message(context, krberr));
266
/* TODO: mem cleanup */
267
if (keytab) krb5_kt_close(context, keytab);
268
if (context) krb5_free_context(context);
272
int ldap_sasl_interact(LDAP *ld, unsigned flags, void *priv_data, void *sit)
274
sasl_interact_t *in = NULL;
275
int ret = LDAP_OTHER;
276
char *realm_name = (char *)priv_data;
278
if (!ld) return LDAP_PARAM_ERROR;
280
for (in = sit; in && in->id != SASL_CB_LIST_END; in++) {
283
in->result = srv_pri_name;
284
in->len = strlen(srv_pri_name);
287
case SASL_CB_GETREALM:
288
in->result = realm_name;
289
in->len = strlen(realm_name);
295
"Unhandled SASL int. option %ld",
306
/* from DS ldaprot.h */
307
#define LDAP_TAG_PWP_WARNING 0xA0 /* context specific + constructed + 0 */
308
#define LDAP_TAG_PWP_SECSLEFT 0x80L /* context specific + primitive */
309
#define LDAP_TAG_PWP_GRCLOGINS 0x81L /* context specific + primitive + 1 */
310
#define LDAP_TAG_PWP_ERROR 0x81L /* context specific + primitive + 1 */
312
int ldap_pwd_change(char *client_name, char *realm_name, krb5_data pwd, char **errstr)
314
char *tmp_file = NULL;
317
BerElement *ctrl = NULL;
318
BerElement *sctrl = NULL;
319
struct berval *control = NULL;
323
struct berval **ncvals;
325
char *attrs[] = {"krbprincipalname", NULL};
326
char *root_attrs[] = {"namingContexts", NULL};
329
struct berval *retdata = NULL;
331
LDAPMessage *entry, *res = NULL;
332
LDAPControl **srvctrl = NULL;
333
char *exterr0 = NULL;
334
char *exterr1 = NULL;
335
char *exterr2 = NULL;
340
int kpwd_err = KRB5_KPASSWD_HARDERROR;
343
tmp_file = strdup(TMP_TEMPLATE);
345
syslog(LOG_ERR, "Out of memory!");
349
fd = mkstemp(tmp_file);
352
"Failed to create tmp file with errno: %d", errno);
355
/* close mimmediately, we don't need to keep the file open,
356
* just that it exist and has a unique name */
359
/* In the long term we may want to do this in the main daemon
360
* and just renew when needed.
361
* Right now do it at every password change for robustness */
362
ret = get_krb5_ticket(tmp_file);
364
syslog(LOG_ERR, "Unable to kinit!");
368
newpw.bv_len = pwd.length;
369
newpw.bv_val = pwd.data;
371
/* retrieve server name and build uri */
372
ret = gethostname(hostname, 1023);
374
syslog(LOG_ERR, "Unable to get the hostname!");
378
ret = asprintf(&uri, "ldap://%s:389", hostname);
380
syslog(LOG_ERR, "Out of memory!");
384
/* connect to ldap server */
385
/* TODO: support referrals ? */
386
ret = ldap_initialize(&ld, uri);
388
if(ret != LDAP_SUCCESS) {
389
syslog(LOG_ERR, "Unable to connect to ldap server: %s",
390
ldap_err2string(ret));
394
version = LDAP_VERSION3;
395
ret = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version);
396
if (ret != LDAP_SUCCESS) {
397
syslog(LOG_ERR, "Unable to set ldap protocol version");
401
ret = ldap_sasl_interactive_bind_s(ld,
405
ldap_sasl_interact, realm_name);
406
if (ret != LDAP_SUCCESS) {
407
syslog(LOG_ERR, "Unable to bind to ldap server");
415
ret = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE,
416
"objectclass=*", root_attrs, 0,
417
NULL, NULL, &tv, 0, &res);
419
if (ret != LDAP_SUCCESS) {
421
"Search for %s on rootdse failed with error %d",
426
/* for now just use the first result we get */
427
entry = ldap_first_entry(ld, res);
428
ncvals = ldap_get_values_len(ld, entry, root_attrs[0]);
430
syslog(LOG_ERR, "No values for %s", root_attrs[0]);
438
ret = asprintf(&filter, "krbPrincipalName=%s", client_name);
440
syslog(LOG_ERR, "Out of memory!");
447
for (i = 0; !userdn && ncvals[i]; i++) {
448
ret = ldap_search_ext_s(ld, ncvals[i]->bv_val,
449
LDAP_SCOPE_SUBTREE, filter, attrs, 1,
450
NULL, NULL, &tv, 0, &res);
452
if (ret != LDAP_SUCCESS) {
456
/* for now just use the first result we get */
457
entry = ldap_first_entry(ld, res);
459
userdn = ldap_get_dn(ld, entry);
466
ldap_value_free_len(ncvals);
468
if (ret != LDAP_SUCCESS) {
469
syslog(LOG_ERR, "Search for %s failed with error %d",
471
if (ret == LDAP_CONSTRAINT_VIOLATION) {
472
*errstr = strdup("Password Change Failed");
473
kpwd_err = KRB5_KPASSWD_SOFTERROR;
482
syslog(LOG_ERR, "No userdn, can't change password!");
486
/* build password change control */
487
ctrl = ber_alloc_t(LBER_USE_DER);
489
syslog(LOG_ERR, "Out of memory!");
493
ret = ber_printf(ctrl, "{tstO}",
494
LDAP_TAG_EXOP_MODIFY_PASSWD_ID, userdn,
495
LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, &newpw);
497
syslog(LOG_ERR, "ber printf failed!");
501
ret = ber_flatten(ctrl, &control);
503
syslog(LOG_ERR, "ber flattening failed!");
507
/* perform password change */
508
ret = ldap_extended_operation(ld,
509
LDAP_EXOP_MODIFY_PASSWD,
512
if (ret != LDAP_SUCCESS) {
513
syslog(LOG_ERR, "ldap_extended_operation() failed. (%d)", ret);
520
ret = ldap_result(ld, msgid, 1, &tv, &res);
522
ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &rc);
523
syslog(LOG_ERR, "ldap_result() failed. (%d)", rc);
527
ret = ldap_parse_extended_result(ld, res, &retoid, &retdata, 0);
528
if(ret != LDAP_SUCCESS) {
529
syslog(LOG_ERR, "ldap_parse_extended_result() failed.");
533
if (retoid || retdata) {
534
syslog(LOG_ERR, "ldap_parse_extended_result() returned data, but we don't handle it yet.");
537
ret = ldap_parse_result(ld, res, &rc, NULL, &err, NULL, &srvctrl, 0);
538
if(ret != LDAP_SUCCESS) {
539
syslog(LOG_ERR, "ldap_parse_result() failed.");
542
if (rc != LDAP_SUCCESS) {
543
if (rc == LDAP_CONSTRAINT_VIOLATION) {
544
kpwd_err = KRB5_KPASSWD_SOFTERROR;
546
ret = LDAP_OPERATIONS_ERROR;
549
syslog(LOG_ERR, "ldap_parse_result(): [%s]", err);
555
LDAPControl *pprc = NULL;
558
for (i = 0; srvctrl[i]; i++) {
559
if (0 == strcmp(srvctrl[i]->ldctl_oid, LDAP_CONTROL_PASSWORDPOLICYRESPONSE)) {
564
sctrl = ber_init(&pprc->ldctl_value);
569
* PasswordPolicyResponseValue ::= SEQUENCE {
570
* warning [0] CHOICE OPTIONAL {
571
* timeBeforeExpiration [0] INTEGER (0 .. maxInt),
572
* graceLoginsRemaining [1] INTEGER (0 .. maxInt) }
573
* error [1] ENUMERATED OPTIONAL {
574
* passwordExpired (0),
576
* changeAfterReset (2),
577
* passwordModNotAllowed (3),
578
* mustSupplyOldPassword (4),
579
* invalidPasswordSyntax (5),
580
* passwordTooShort (6),
581
* passwordTooYoung (7),
582
* passwordInHistory (8) } }
585
ber_tag_t rtag, btag;
587
rtag = ber_scanf(sctrl, "{t", &btag);
588
if (rtag == LBER_ERROR) {
589
syslog(LOG_ERR, "Could not decode the tag BER element");
593
if (btag == LDAP_TAG_PWP_WARNING) {
594
rtag = ber_scanf(sctrl, "{ti}", &btag, &bint);
595
if (rtag == LBER_ERROR) {
596
syslog(LOG_ERR, "Could not decode the warning BER element");
599
if (btag == LDAP_TAG_PWP_SECSLEFT) {
600
ret = asprintf(&exterr2, " (%d seconds left before password expires)", bint);
602
ret = asprintf(&exterr2, " (%d grace logins remaining)", bint);
605
syslog(LOG_ERR, "OOM while creating error message ...");
608
/* The next element might or might not be there (the control is a sequence) */
609
ber_scanf(sctrl, "t", &btag);
611
if (btag == LDAP_TAG_PWP_ERROR) {
612
rtag = ber_scanf(sctrl, "e", &bint);
613
if (rtag == LBER_ERROR) {
614
syslog(LOG_ERR, "Could not decode the error BER element");
620
ret = asprintf(&exterr1, " Err%d: Password Expired.", bint);
623
ret = asprintf(&exterr1, " Err%d: Account locked.", bint);
626
ret = asprintf(&exterr1, " Err%d: Password changed after reset.", bint);
629
ret = asprintf(&exterr1, " Err%d: Password change not allowed.", bint);
632
ret = asprintf(&exterr1, " Err%d: [Shouldn't happen].", bint);
635
ret = asprintf(&exterr1, " Err%d: Password too simple.", bint);
638
ret = asprintf(&exterr1, " Err%d: Password too short.", bint);
641
ret = asprintf(&exterr1, " Err%d: Too soon to change password.", bint);
644
ret = asprintf(&exterr1, " Err%d: Password reuse not permitted.", bint);
647
ret = asprintf(&exterr1, " Err%d: Unknown Errorcode.", bint);
651
syslog(LOG_ERR, "OOM while creating error message ...");
658
if (ret == LDAP_SUCCESS) {
659
kpwd_err = KRB5_KPASSWD_SUCCESS;
660
exterr0 = "Password change succeeded";
662
exterr0 = "Password change failed";
664
ret = asprintf(errstr, "%s%s%s", exterr0, exterr1?exterr1:"", exterr2?exterr2:"");
666
syslog(LOG_ERR, "OOM while creating error message ...");
671
if (ctrl) ber_free(ctrl, 1);
672
if (sctrl) ber_free(sctrl, 1);
673
if (srvctrl) ldap_controls_free(srvctrl);
674
if (res) ldap_msgfree(res);
675
if (control) ber_bvfree(control);
680
if (ld) ldap_unbind_ext(ld, NULL, NULL);
688
void handle_krb_packets(uint8_t *buf, ssize_t buflen,
690
struct sockaddr_storage *from,
691
uint8_t **repbuf, ssize_t *replen)
693
krb5_auth_context auth_context;
694
krb5_context context;
696
krb5_principal kprincpw;
698
krb5_address lkaddr, rkaddr;
699
krb5_data kreq, krep, kenc, kdec;
700
krb5_replay_data replay;
705
char *client_name, *realm_name;
713
result_string = NULL;
723
switch(((struct sockaddr *)from)->sa_family) {
725
lkaddr.addrtype = ADDRTYPE_INET;
726
lkaddr.length = sizeof(((struct sockaddr_in *)&sd->dest_addr)->sin_addr);
727
lkaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&sd->dest_addr)->sin_addr);
729
rkaddr.addrtype = ADDRTYPE_INET;
730
rkaddr.length = sizeof(((struct sockaddr_in *)from)->sin_addr);
731
rkaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)from)->sin_addr);
734
if (IN6_IS_ADDR_V4MAPPED (&((struct sockaddr_in6 *)from)->sin6_addr)) {
735
lkaddr.addrtype = ADDRTYPE_INET;
737
lkaddr.contents = 12 + (krb5_octet *) &(((struct sockaddr_in6 *)&sd->dest_addr)->sin6_addr);
739
rkaddr.addrtype = ADDRTYPE_INET;
741
rkaddr.contents = 12 + (krb5_octet *) &(((struct sockaddr_in6 *)from)->sin6_addr);
743
lkaddr.addrtype = ADDRTYPE_INET6;
744
lkaddr.length = sizeof(((struct sockaddr_in6 *)&sd->dest_addr)->sin6_addr);
745
lkaddr.contents = (krb5_octet *) &(((struct sockaddr_in6 *)&sd->dest_addr)->sin6_addr);
747
rkaddr.addrtype = ADDRTYPE_INET6;
748
rkaddr.length = sizeof(((struct sockaddr_in6 *)from)->sin6_addr);
749
rkaddr.contents = (krb5_octet *) &(((struct sockaddr_in6 *)from)->sin6_addr);
753
result_string = strdup("Invalid remopte IP address");
754
result_err = KRB5_KPASSWD_MALFORMED;
755
syslog(LOG_ERR, "%s", result_string);
760
result_string = strdup("Request truncated");
761
result_err = KRB5_KPASSWD_MALFORMED;
762
syslog(LOG_ERR, "%s", result_string);
766
reqlen = (buf[0] << 8) + buf[1];
768
if (reqlen != buflen) {
769
result_string = strdup("Unmatching request length");
770
result_err = KRB5_KPASSWD_MALFORMED;
771
syslog(LOG_ERR, "%s", result_string);
775
verno = (buf[2] << 8) + buf[3];
778
result_string = strdup("Unsupported version");
779
result_err = KRB5_KPASSWD_BAD_VERSION;
780
syslog(LOG_ERR, "%s", result_string);
784
kreq.length = (buf[4] << 8) + buf[5];
785
if (kreq.length > (buflen - 6)) {
786
result_string = strdup("Request truncated");
787
result_err = KRB5_KPASSWD_MALFORMED;
788
syslog(LOG_ERR, "%s", result_string);
791
kreq.data = (char *)&buf[6];
793
krberr = krb5_init_context(&context);
795
result_string = strdup("Failed to init kerberos context");
796
result_err = KRB5_KPASSWD_HARDERROR;
797
syslog(LOG_ERR, "%s", result_string);
801
krberr = krb5_get_default_realm(context, &realm_name);
803
result_string = strdup("Failed to get default realm name");
804
result_err = KRB5_KPASSWD_HARDERROR;
805
syslog(LOG_ERR, "%s", result_string);
809
krberr = krb5_auth_con_init(context, &auth_context);
811
result_string = strdup("Unable to init auth context");
812
result_err = KRB5_KPASSWD_HARDERROR;
813
syslog(LOG_ERR, "%s: %s", result_string,
814
krb5_get_error_message(context, krberr));
818
krberr = krb5_auth_con_setflags(context, auth_context,
819
KRB5_AUTH_CONTEXT_DO_SEQUENCE);
821
result_string = strdup("Unable to init auth context");
822
result_err = KRB5_KPASSWD_HARDERROR;
823
syslog(LOG_ERR, "%s: %s", result_string,
824
krb5_get_error_message(context, krberr));
828
krberr = krb5_build_principal(context, &kprincpw,
829
strlen(realm_name), realm_name,
830
"kadmin", "changepw", NULL);
832
result_string = strdup("Unable to build principal");
833
result_err = KRB5_KPASSWD_HARDERROR;
834
syslog(LOG_ERR, "%s: %s", result_string,
835
krb5_get_error_message(context, krberr));
839
krberr = krb5_kt_resolve(context, keytab_name, &keytab);
841
result_string = strdup("Unable to retrieve keytab");
842
result_err = KRB5_KPASSWD_HARDERROR;
843
syslog(LOG_ERR, "%s: %s", result_string,
844
krb5_get_error_message(context, krberr));
848
krberr = krb5_rd_req(context, &auth_context, &kreq,
849
kprincpw, keytab, NULL, &ticket);
851
result_string = strdup("Unable to read request");
852
result_err = KRB5_KPASSWD_AUTHERROR;
853
syslog(LOG_ERR, "%s: %s", result_string,
854
krb5_get_error_message(context, krberr));
858
/* build the AP Reply before actually changing the password
859
* this minimize the risk of a fatal error occurring _after_
860
* the password have been successfully changed */
861
krberr = krb5_mk_rep(context, auth_context, &krep);
863
result_string = strdup("Failed to to build reply");
864
result_err = KRB5_KPASSWD_HARDERROR;
865
syslog(LOG_ERR, "%s: %s", result_string,
866
krb5_get_error_message(context, krberr));
870
/* verify that this is an AS_REQ ticket */
871
if (!(ticket->enc_part2->flags & TKT_FLG_INITIAL)) {
872
result_string = strdup("Ticket must be derived from a password");
873
result_err = KRB5_KPASSWD_AUTHERROR;
874
syslog(LOG_ERR, "%s", result_string);
878
krberr = krb5_unparse_name(context, ticket->enc_part2->client,
881
result_string = strdup("Unable to parse client name");
882
result_err = KRB5_KPASSWD_HARDERROR;
883
syslog(LOG_ERR, "%s", result_string);
887
krberr = krb5_auth_con_setaddrs(context, auth_context, NULL, &rkaddr);
889
result_string = strdup("Failed to set client address");
890
result_err = KRB5_KPASSWD_HARDERROR;
891
syslog(LOG_ERR, "%s: %s", result_string,
892
krb5_get_error_message(context, krberr));
896
/* decrypt the new password */
897
kenc.length = reqlen - kreq.length - 6;
898
kenc.data = kreq.data + kreq.length;
900
/* rd_priv needs the remote address while mk_priv (used later)
901
* requires the local address (from kadmin code) */
902
krberr = krb5_rd_priv(context, auth_context, &kenc, &kdec, &replay);
904
result_string = strdup("Failed to decrypt password");
905
result_err = KRB5_KPASSWD_HARDERROR;
906
syslog(LOG_ERR, "%s: %s", result_string,
907
krb5_get_error_message(context, krberr));
912
syslog(LOG_ERR, "Client %s trying to set password [%*s]",
913
client_name, kdec.length, kdec.data);
916
/* Actually try to change the password */
917
result_err = ldap_pwd_change(client_name, realm_name, kdec, &result_string);
918
if (result_string == NULL) {
919
result_string = strdup("Server Error while performing LDAP password change");
921
syslog(LOG_ERR, "%s", result_string);
923
/* make sure password is cleared off before we free the memory */
924
memset(kdec.data, 0, kdec.length);
930
/* set-up the clear text reply */
931
kdec.length = 2 + strlen(result_string);
932
kdec.data = malloc(kdec.length);
934
syslog(LOG_ERR, "Out of memory!");
939
kdec.data[0] = (result_err >> 8) & 0xff;
940
kdec.data[1] = result_err & 0xff;
941
memcpy(&kdec.data[2], result_string, strlen(result_string));
944
result_string = NULL;
946
krberr = krb5_auth_con_setaddrs(context, auth_context, &lkaddr, NULL);
948
result_string = strdup("Failed to set local address");
949
syslog(LOG_ERR, "%s: %s", result_string,
950
krb5_get_error_message(context, krberr));
954
krberr = krb5_mk_priv(context, auth_context, &kdec, &kenc, &replay);
956
result_string = strdup("Failed to encrypt reply message");
957
syslog(LOG_ERR, "%s: %s", result_string,
958
krb5_get_error_message(context, krberr));
961
result_string = NULL;
962
/* encryption was unsuccessful, let's return a krb error */
964
/* the ap data is no more useful */
968
/* build a krberror encrypted paylod */
969
krb5err.error = KRB5_CHPW_FAIL;
970
krb5err.server = kprincpw;
971
krb5err.client = NULL;
975
krberr = krb5_timeofday(context, &krb5err.stime);
977
result_string = strdup("Failed to set time of day");
978
syslog(LOG_ERR, "%s: %s", result_string,
979
krb5_get_error_message(context, krberr));
983
krb5err.text.length = 0;
984
krb5err.e_data = kdec;
985
krberr = krb5_mk_error(context, &krb5err, &kenc);
987
result_string = strdup("Failed to build error message");
988
syslog(LOG_ERR, "%s: %s", result_string,
989
krb5_get_error_message(context, krberr));
994
replylen = 6 + krep.length + kenc.length;
995
reply = malloc(replylen);
997
syslog(LOG_ERR, "Out of memory!");
1002
reply[0] = (replylen >> 8) & 0xff;
1003
reply[1] = replylen & 0xff;
1006
reply[4] = (krep.length >> 8) & 0xff;
1007
reply[5] = krep.length & 0xff;
1010
memcpy(&reply[6], krep.data, krep.length);
1012
memcpy(&reply[6 + krep.length], kenc.data, kenc.length);
1017
free(result_string);
1018
if (auth_context) krb5_auth_con_free(context, auth_context);
1019
if (kprincpw) krb5_free_principal(context, kprincpw);
1020
if (krep.length) free(krep.data);
1021
if (ticket) krb5_free_ticket(context, ticket);
1022
if (kdec.length) free(kdec.data);
1023
if (context) krb5_free_context(context);
1026
pid_t handle_conn(struct socklist *sd)
1030
char addrto6[INET6_ADDRSTRLEN+1];
1031
char address[INET6_ADDRSTRLEN+1];
1032
uint8_t request[1500];
1036
struct sockaddr_storage from;
1041
fromlen = sizeof(from);
1046
/* receive request */
1047
if (sd->socktype == SOCK_STREAM) {
1049
mfd = accept(sd->fd, (struct sockaddr *)&from, &fromlen);
1051
syslog(LOG_ERR, "Accept failed with error (%d) %s",
1052
errno, strerror(errno));
1056
/* read first to empty the buffer on udp connections */
1057
reqlen = recvfrom(sd->fd, request, sizeof(request), 0,
1058
(struct sockaddr *)&from, &fromlen);
1060
syslog(LOG_ERR, "Error receiving request (%d) %s",
1061
errno, strerror(errno));
1067
ret = getnameinfo((struct sockaddr *)&from, fromlen,
1068
addrto6, INET6_ADDRSTRLEN+1,
1069
NULL, 0, NI_NUMERICHOST);
1071
syslog(LOG_ERR, "Error retrieving host address\n");
1076
syslog(LOG_ERR, "Connection from %s", addrto6);
1079
if (strchr(addrto6, ':') == NULL) {
1080
char *prefix6 = "::ffff:";
1081
/* this is an IPv4 formatted addr
1082
* convert to IPv6 mapped addr */
1083
memcpy(address, prefix6, 7);
1084
memcpy(&address[7], addrto6, INET6_ADDRSTRLEN-7);
1086
/* regular IPv6 address, copy as is */
1087
memcpy(address, addrto6, INET6_ADDRSTRLEN);
1089
/* make sure we have termination */
1090
address[INET6_ADDRSTRLEN] = '\0';
1092
/* Check blacklist for requests from the same IP until operations
1093
* are finished on the active client.
1094
* the password change may be slow and pam_krb5 sends up to 3 UDP
1095
* requests waiting 1 sec. each time.
1096
* We do not want to start 3 password changes at the same time */
1098
if (check_blacklist(address)) {
1100
syslog(LOG_ERR, "[%s] blacklisted", address);
1102
if (tcp) close(mfd);
1106
/* now read data if it was a TCP connection */
1108
reqlen = recvfrom(mfd, request, sizeof(request), 0,
1109
(struct sockaddr *)&from, &fromlen);
1111
syslog(LOG_ERR, "Error receiving request (%d) %s",
1112
errno, strerror(errno));
1118
/* handle kerberos and ldap operations in childrens */
1121
syslog(LOG_ERR, "Fork failed with error (%d) %s",
1122
errno, strerror(errno));
1123
if (tcp) close(mfd);
1126
if (pid != 0) { /* parent */
1127
if (tcp) close(mfd);
1128
add_blacklist(pid, address);
1134
if (debug > 0) syslog(LOG_ERR, "Servicing %s", address);
1136
/* TCP packets prepend the lenght as a 32bit network order field,
1137
* this information seem to be just redundant, so let's simply
1140
handle_krb_packets(request+4, reqlen-4, sd, &from, &reply, &replen);
1142
handle_krb_packets(request, reqlen, sd, &from, &reply, &replen);
1145
if (replen) { /* we have something to reply */
1147
sendret = sendto(mfd, reply, replen, 0, NULL, 0);
1149
sendret = sendto(sd->fd, reply, replen, 0, (struct sockaddr *)&from, fromlen);
1151
if (sendret == -1) {
1152
syslog(LOG_ERR, "Error sending reply (%d)", errno);
1155
if (tcp) close(mfd);
1159
static int create_socket(struct addrinfo *ai, struct socklist **_sds,
1160
struct pollfd **_pfds, int *_nfds)
1162
struct socklist *csd, *tsd;
1163
struct pollfd *pfds;
1171
csd = calloc(1, sizeof(struct socklist));
1173
syslog(LOG_ERR, "Out of memory, can't create socklist\n");
1176
csd->socktype = ai->ai_socktype;
1177
csd->dest_addr_len = ai->ai_addrlen;
1178
memcpy(&csd->dest_addr, ai->ai_addr, ai->ai_addrlen);
1180
csd->fd = socket(csd->dest_addr.ss_family, csd->socktype, 0);
1181
if (csd->fd == -1) {
1182
syslog(LOG_ERR, "Unable to create socket (%s)",
1186
ret = setsockopt(csd->fd, SOL_SOCKET, SO_REUSEADDR,
1187
(void *)&tru, sizeof(tru));
1189
ret = bind(csd->fd, (struct sockaddr *)&csd->dest_addr, csd->dest_addr_len);
1191
if (errno != EADDRINUSE) {
1192
syslog(LOG_ERR, "Unable to bind to socket");
1196
/* if EADDRINUSE it means we are on a machine
1197
* with a dual ipv4/ipv6 stack that does not
1198
* allow to bind on both at the same time as the
1199
* ipv6 bind already allows connections on ipv4
1206
if (csd->socktype == SOCK_STREAM) {
1207
ret = listen(csd->fd, SOMAXCONN);
1209
syslog(LOG_ERR, "Unable to listen to TCP socket (%s)",
1216
pfds = realloc(pfds, sizeof(struct pollfd) * (nfds +1));
1218
syslog(LOG_ERR, "Out of memory, can't alloc pollfd array\n");
1222
pfds[nfds].events = POLLIN;
1223
pfds[nfds].fd = csd->fd;
1227
for (tsd = *_sds; tsd->next; tsd = tsd->next) /* skip */ ;
1243
int main(int argc, char *argv[])
1246
struct ifaddrs *ifa, *tifa;
1247
struct addrinfo *ai, *tai;
1248
struct addrinfo hints;
1249
char host[NI_MAXHOST];
1250
struct socklist *sds, *csd;
1251
struct pollfd *pfds;
1257
openlog("kpasswd", LOG_PID, LOG_DAEMON);
1259
/* do not keep any fs busy */
1262
syslog(LOG_ERR, "Unable to change dir to '/'");
1269
syslog(LOG_ERR, "Error fork() failed!");
1272
if (pid != 0) { /* parent */
1279
/* close std* descriptors */
1284
/* fork again to make sure we completely detach from parent process */
1287
syslog(LOG_ERR, "Error fork() failed!");
1290
if (pid != 0) { /* parent */
1294
/* source env vars */
1295
env = getenv("KRB5_KTNAME");
1297
env = DEFAULT_KEYTAB;
1299
keytab_name = strdup(env);
1301
syslog(LOG_ERR, "Out of memory!");
1304
env = getenv("IPA_KPASSWD_DEBUG");
1306
debug = strtol(env, NULL, 0);
1309
ret = getifaddrs(&ifa);
1311
syslog(LOG_ERR, "getifaddrs failed: %s", gai_strerror(ret));
1315
/* Write out the pid file after the sigterm handler */
1316
const char *pid_file = "/var/run/ipa_kpasswd.pid";
1317
FILE *f = fopen(pid_file, "w");
1320
int n_bytes = fprintf(f, "%ld\n", (long) getpid());
1321
if (fclose(f) == 0 && 0 < n_bytes)
1325
syslog(LOG_ERR, "Couldn't create pid file %s: %s",
1326
pid_file, strerror(errno));
1334
for (tifa = ifa; tifa; tifa = tifa->ifa_next) {
1336
if (NULL == tifa->ifa_addr)
1337
/* uhmm no address ?? skip it */
1340
if (tifa->ifa_addr->sa_family != AF_INET &&
1341
tifa->ifa_addr->sa_family != AF_INET6) {
1342
/* not interesting for us */
1346
ret = getnameinfo(tifa->ifa_addr, sizeof(struct sockaddr_storage),
1347
host, sizeof(host), NULL, 0, NI_NUMERICHOST);
1349
syslog(LOG_ERR, "Error converting address (%s)",
1353
syslog(LOG_INFO, "Setting up socket for [%s]", host);
1356
memset(&hints, 0, sizeof(hints));
1357
hints.ai_flags = AI_NUMERICHOST;
1358
hints.ai_family = AF_UNSPEC;
1360
/* this should return 2 entries, one for UDP and one for TCP */
1361
ret = getaddrinfo(host, "kpasswd", &hints, &ai);
1363
syslog(LOG_ERR, "Error getting address info (%s) for [%s]",
1364
gai_strerror(ret), host);
1368
for (tai = ai; tai; tai = tai->ai_next) {
1369
char *socktype = (tai->ai_socktype==SOCK_STREAM)?"TCP":"UDP";
1370
ret = create_socket(tai, &sds, &pfds, &nfds);
1373
"Failed to set up %s socket for [%s]",
1380
syslog(LOG_ERR, "Failed to setup any socket. Aborting");
1384
/* now that sockets are set up, enter the poll loop */
1387
int cstatus, cid, i;
1389
ret = poll(pfds, nfds, 3000);
1395
if (errno != EINTR) {
1397
"Unexpected error in poll (%d) %s",
1398
errno, strerror(errno));
1403
for (i = 0; i < nfds; i++) {
1404
if (pfds[i].revents & POLLIN) {
1405
for (csd = sds; csd; csd = csd->next) {
1406
if (csd->fd == pfds[i].fd) {
1414
/* check for children exiting */
1415
cid = waitpid(-1, &cstatus, WNOHANG);
1416
if (cid != -1 && cid != 0) {
1418
syslog(LOG_ERR, "pid %d completed operations!\n", cid);
1419
remove_blacklist(cid);