~ubuntu-branches/ubuntu/trusty/freeipa/trusty

« back to all changes in this revision

Viewing changes to daemons/ipa-kpasswd/ipa_kpasswd.c

  • Committer: Package Import Robot
  • Author(s): Timo Aaltonen
  • Date: 2013-03-07 14:10:03 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20130307141003-kz4lq9vj4x692mqq
Tags: 3.1.2-0ubuntu1
* Merge from unreleased debian git.
  - new upstream release
  - doesn't use chkconfig anymore (LP: #1025018, #1124093)
  - drop -U from the ntpdate options (LP: #1149468)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
 
2
 
/* Kpasswd-LDAP proxy */
3
 
 
4
 
/* Authors: Simo Sorce <ssorce@redhat.com>
5
 
 *
6
 
 * Copyright (C) 2007, 2008  Red Hat
7
 
 * see file 'COPYING' for use and warranty information
8
 
 *
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.
13
 
 *
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.
18
 
 *
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/>.
21
 
 */
22
 
 
23
 
#define _GNU_SOURCE
24
 
 
25
 
#include <sys/types.h>
26
 
#include <sys/socket.h>
27
 
#include <sys/time.h>
28
 
#include <sys/wait.h>
29
 
#include <sys/poll.h>
30
 
#include <unistd.h>
31
 
#include <stdio.h>
32
 
#include <stdarg.h>
33
 
#include <stdlib.h>
34
 
#include <string.h>
35
 
#include <errno.h>
36
 
#include <netdb.h>
37
 
#include <syslog.h>
38
 
#include <netinet/in.h>
39
 
#include <arpa/inet.h>
40
 
#include <time.h>
41
 
#include <krb5.h>
42
 
#include <ldap.h>
43
 
#include <sasl/sasl.h>
44
 
#include <ifaddrs.h>
45
 
 
46
 
#define DEFAULT_KEYTAB "FILE:/var/kerberos/krb5kdc/kpasswd.keytab"
47
 
#define TMP_TEMPLATE "/var/cache/ipa/kpasswd/krb5_cc.XXXXXX"
48
 
 
49
 
/* blacklist entries are released only BLCAKLIST_TIMEOUT seconds
50
 
 * after the children performing the noperation has finished.
51
 
 * this is to avoid races */
52
 
 
53
 
#define BLACKLIST_TIMEOUT 5
54
 
 
55
 
struct blacklist {
56
 
        struct blacklist *next;
57
 
        char *address;
58
 
        pid_t pid;
59
 
        time_t expire;
60
 
};
61
 
 
62
 
static struct blacklist *global_blacklist = NULL;
63
 
 
64
 
struct socklist {
65
 
        int fd;
66
 
        int socktype;
67
 
        int dest_addr_len;
68
 
        struct sockaddr_storage dest_addr;
69
 
        struct socklist *next;
70
 
};
71
 
 
72
 
int check_blacklist(char *address)
73
 
{
74
 
        struct blacklist *bl, *prev_bl;
75
 
        time_t now = time(NULL);
76
 
 
77
 
        if (!global_blacklist) {
78
 
                return 0;
79
 
        }
80
 
 
81
 
        prev_bl = NULL;
82
 
        bl = global_blacklist;
83
 
        while (bl) {
84
 
                if (bl->expire && (bl->expire < now)) {
85
 
                        if (prev_bl) {
86
 
                                prev_bl->next = bl->next;
87
 
                                free(bl->address);
88
 
                                free(bl);
89
 
                                bl = prev_bl->next;
90
 
                        } else {
91
 
                                global_blacklist = bl->next;
92
 
                                free(bl->address);
93
 
                                free(bl);
94
 
                                bl = global_blacklist;
95
 
                        }
96
 
                        continue;
97
 
                }
98
 
 
99
 
                if (strcmp(address, bl->address) == 0) {
100
 
                        return 1;
101
 
                }
102
 
 
103
 
                prev_bl = bl;
104
 
                bl = bl->next;
105
 
        }
106
 
 
107
 
        return 0;
108
 
}
109
 
 
110
 
int add_blacklist(pid_t pid, char *address)
111
 
{
112
 
        struct blacklist *bl, *gbl;
113
 
 
114
 
        bl = malloc(sizeof(struct blacklist));
115
 
        if (!bl) return -1;
116
 
 
117
 
        bl->next = NULL;
118
 
        bl->pid = pid;
119
 
        bl->expire = 0;
120
 
        bl->address = strdup(address);
121
 
        if (!bl->address) {
122
 
                free(bl);
123
 
                return -1;
124
 
        }
125
 
 
126
 
        if (!global_blacklist) {
127
 
                global_blacklist = bl;
128
 
                return 0;
129
 
        }
130
 
 
131
 
        gbl = global_blacklist;
132
 
        while (gbl->next) {
133
 
                gbl = gbl->next;
134
 
        }
135
 
        gbl->next = bl;
136
 
        return 0;
137
 
}
138
 
 
139
 
int remove_blacklist(pid_t pid)
140
 
{
141
 
        struct blacklist *bl;
142
 
 
143
 
        if (!global_blacklist) {
144
 
                return -1;
145
 
        }
146
 
 
147
 
        bl = global_blacklist;
148
 
        while (bl) {
149
 
                if (pid == bl->pid) {
150
 
                        bl->expire = time(NULL) + BLACKLIST_TIMEOUT;
151
 
                        return 0;
152
 
                }
153
 
                bl = bl->next;
154
 
        }
155
 
        return -1;
156
 
}
157
 
 
158
 
int debug = 0;
159
 
char *srv_pri_name = "kadmin/changepw";
160
 
char *keytab_name = NULL;
161
 
 
162
 
static int get_krb5_ticket(char *tmp_file)
163
 
{
164
 
        char *ccname;
165
 
        char *realm_name = NULL;
166
 
        krb5_context context = NULL;
167
 
        krb5_keytab keytab = NULL;
168
 
        krb5_ccache ccache = NULL;
169
 
        krb5_principal kprincpw;
170
 
        krb5_creds my_creds;
171
 
        krb5_get_init_creds_opt options;
172
 
        krb5_error_code krberr;
173
 
        int ret;
174
 
 
175
 
        krberr = krb5_init_context(&context);
176
 
        if (krberr) {
177
 
                syslog(LOG_ERR, "Failed to init kerberos context");
178
 
                return -1;
179
 
        }
180
 
 
181
 
        krberr = krb5_get_default_realm(context, &realm_name);
182
 
        if (krberr) {
183
 
                syslog(LOG_ERR, "Failed to get default realm name: %s",
184
 
                        krb5_get_error_message(context, krberr));
185
 
                ret = -1;
186
 
                goto done;
187
 
        }
188
 
 
189
 
        krberr = krb5_build_principal(context, &kprincpw,
190
 
                                      strlen(realm_name), realm_name,
191
 
                                      "kadmin", "changepw", NULL);
192
 
        if (krberr) {
193
 
                syslog(LOG_ERR, "Unable to build principal: %s",
194
 
                        krb5_get_error_message(context, krberr));
195
 
                ret = -1;
196
 
                goto done;
197
 
        }
198
 
 
199
 
        krberr = krb5_kt_resolve(context, keytab_name, &keytab);
200
 
        if (krberr) {
201
 
                syslog(LOG_ERR, "Failed to read keytab file: %s",
202
 
                        krb5_get_error_message(context, krberr));
203
 
                ret = -1;
204
 
                goto done;
205
 
        }
206
 
 
207
 
        ret = asprintf(&ccname, "FILE:%s", tmp_file);
208
 
        if (ret == -1) {
209
 
                syslog(LOG_ERR, "Out of memory!");
210
 
                goto done;
211
 
        }
212
 
 
213
 
        ret = setenv("KRB5CCNAME", ccname, 1);
214
 
        if (ret == -1) {
215
 
                syslog(LOG_ERR, "Unable to set env. variable KRB5CCNAME!");
216
 
                goto done;
217
 
        }
218
 
 
219
 
        krberr = krb5_cc_resolve(context, ccname, &ccache);
220
 
        if (krberr) {
221
 
                syslog(LOG_ERR, "Failed to set cache name: %s",
222
 
                        krb5_get_error_message(context, krberr));
223
 
                ret = -1;
224
 
                goto done;
225
 
        }
226
 
 
227
 
        memset(&my_creds, 0, sizeof(my_creds));
228
 
        memset(&options, 0, sizeof(options));
229
 
 
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);
235
 
 
236
 
        krberr = krb5_get_init_creds_keytab(context, &my_creds, kprincpw,
237
 
                                          keytab, 0, NULL,
238
 
                                          &options);
239
 
 
240
 
        if (krberr) {
241
 
                syslog(LOG_ERR, "Failed to init credentials: %s",
242
 
                        krb5_get_error_message(context, krberr));
243
 
                ret = -1;
244
 
                goto done;
245
 
        }
246
 
 
247
 
        krberr = krb5_cc_initialize(context, ccache, kprincpw);
248
 
        if (krberr) {
249
 
                syslog(LOG_ERR, "Failed to init ccache: %s",
250
 
                        krb5_get_error_message(context, krberr));
251
 
                ret = -1;
252
 
                goto done;
253
 
        }
254
 
 
255
 
        krberr = krb5_cc_store_cred(context, ccache, &my_creds);
256
 
        if (krberr) {
257
 
                syslog(LOG_ERR, "Failed to store creds: %s",
258
 
                        krb5_get_error_message(context, krberr));
259
 
                ret = -1;
260
 
                goto done;
261
 
        }
262
 
 
263
 
        ret = 0;
264
 
 
265
 
done:
266
 
        /* TODO: mem cleanup */
267
 
        if (keytab) krb5_kt_close(context, keytab);
268
 
        if (context) krb5_free_context(context);
269
 
        return ret;
270
 
}
271
 
 
272
 
int ldap_sasl_interact(LDAP *ld, unsigned flags, void *priv_data, void *sit)
273
 
{
274
 
        sasl_interact_t *in = NULL;
275
 
        int ret = LDAP_OTHER;
276
 
        char *realm_name = (char *)priv_data;
277
 
 
278
 
        if (!ld) return LDAP_PARAM_ERROR;
279
 
 
280
 
        for (in = sit; in && in->id != SASL_CB_LIST_END; in++) {
281
 
                switch(in->id) {
282
 
                case SASL_CB_USER:
283
 
                        in->result = srv_pri_name;
284
 
                        in->len = strlen(srv_pri_name);
285
 
                        ret = LDAP_SUCCESS;
286
 
                        break;
287
 
                case SASL_CB_GETREALM:
288
 
                        in->result = realm_name;
289
 
                        in->len = strlen(realm_name);
290
 
                        ret = LDAP_SUCCESS;
291
 
                        break;
292
 
                default:
293
 
                        if (debug > 0) {
294
 
                                syslog(LOG_ERR,
295
 
                                        "Unhandled SASL int. option %ld",
296
 
                                        in->id);
297
 
                        }
298
 
                        in->result = NULL;
299
 
                        in->len = 0;
300
 
                        ret = LDAP_OTHER;
301
 
                }
302
 
        }
303
 
        return ret;
304
 
}
305
 
 
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 */ 
311
 
 
312
 
int ldap_pwd_change(char *client_name, char *realm_name, krb5_data pwd, char **errstr)
313
 
{
314
 
        char *tmp_file = NULL;
315
 
        int version;
316
 
        LDAP *ld = NULL;
317
 
        BerElement *ctrl = NULL;
318
 
        BerElement *sctrl = NULL;
319
 
        struct berval *control = NULL;
320
 
        struct berval newpw;
321
 
        char hostname[1024];
322
 
        char *uri;
323
 
        struct berval **ncvals;
324
 
        char *filter;
325
 
        char *attrs[] = {"krbprincipalname", NULL};
326
 
        char *root_attrs[] = {"namingContexts", NULL};
327
 
        char *userdn = NULL;
328
 
        char *retoid = NULL;
329
 
        struct berval *retdata = NULL;
330
 
        struct timeval tv;
331
 
        LDAPMessage *entry, *res = NULL;
332
 
        LDAPControl **srvctrl = NULL;
333
 
        char *exterr0 = NULL;
334
 
        char *exterr1 = NULL;
335
 
        char *exterr2 = NULL;
336
 
        char *err = NULL;
337
 
        int msgid;
338
 
        int ret, rc;
339
 
        int fd;
340
 
        int kpwd_err = KRB5_KPASSWD_HARDERROR;
341
 
        int i;
342
 
 
343
 
        tmp_file = strdup(TMP_TEMPLATE);
344
 
        if (!tmp_file) {
345
 
                syslog(LOG_ERR, "Out of memory!");
346
 
                goto done;
347
 
        }
348
 
 
349
 
        fd = mkstemp(tmp_file);
350
 
        if (fd == -1) {
351
 
                syslog(LOG_ERR,
352
 
                        "Failed to create tmp file with errno: %d", errno);
353
 
                goto done;
354
 
        }
355
 
        /* close mimmediately, we don't need to keep the file open,
356
 
         * just that it exist and has a unique name */
357
 
        close(fd);
358
 
 
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);
363
 
        if (ret) {
364
 
                syslog(LOG_ERR, "Unable to kinit!");
365
 
                goto done;
366
 
        }
367
 
 
368
 
        newpw.bv_len = pwd.length;
369
 
        newpw.bv_val = pwd.data;
370
 
 
371
 
        /* retrieve server name and build uri */
372
 
        ret = gethostname(hostname, 1023);
373
 
        if (ret == -1) {
374
 
                syslog(LOG_ERR, "Unable to get the hostname!");
375
 
                goto done;
376
 
        }
377
 
 
378
 
        ret = asprintf(&uri, "ldap://%s:389", hostname);
379
 
        if (ret == -1) {
380
 
            syslog(LOG_ERR, "Out of memory!");
381
 
            goto done;
382
 
        }
383
 
 
384
 
        /* connect to ldap server */
385
 
        /* TODO: support referrals ? */
386
 
        ret = ldap_initialize(&ld, uri);
387
 
        free(uri);
388
 
        if(ret != LDAP_SUCCESS) {
389
 
                syslog(LOG_ERR, "Unable to connect to ldap server: %s",
390
 
                                ldap_err2string(ret));
391
 
                goto done;
392
 
        }
393
 
 
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");
398
 
                goto done;
399
 
        }
400
 
 
401
 
        ret = ldap_sasl_interactive_bind_s(ld,
402
 
                                           NULL, "GSSAPI",
403
 
                                           NULL, NULL,
404
 
                                           LDAP_SASL_AUTOMATIC,
405
 
                                           ldap_sasl_interact, realm_name);
406
 
        if (ret != LDAP_SUCCESS) {
407
 
                syslog(LOG_ERR, "Unable to bind to ldap server");
408
 
                goto done;
409
 
        }
410
 
 
411
 
        /* find base dn */
412
 
        tv.tv_sec = 10;
413
 
        tv.tv_usec = 0;
414
 
 
415
 
        ret = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE,
416
 
                                "objectclass=*", root_attrs, 0,
417
 
                                NULL, NULL, &tv, 0, &res);
418
 
 
419
 
        if (ret != LDAP_SUCCESS) {
420
 
                syslog(LOG_ERR,
421
 
                        "Search for %s on rootdse failed with error %d",
422
 
                        root_attrs[0], ret);
423
 
                goto done;
424
 
        }
425
 
 
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]);
429
 
        if (!ncvals) {
430
 
                syslog(LOG_ERR, "No values for %s", root_attrs[0]);
431
 
                goto done;
432
 
        }
433
 
 
434
 
        ldap_msgfree(res);
435
 
        res = NULL;
436
 
 
437
 
        /* find user dn */
438
 
        ret = asprintf(&filter, "krbPrincipalName=%s", client_name);
439
 
        if (ret == -1) {
440
 
                syslog(LOG_ERR, "Out of memory!");
441
 
                goto done;
442
 
        }
443
 
 
444
 
        tv.tv_sec = 10;
445
 
        tv.tv_usec = 0; 
446
 
 
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);
451
 
 
452
 
                if (ret != LDAP_SUCCESS) {
453
 
                        break;
454
 
                }
455
 
 
456
 
                /* for now just use the first result we get */
457
 
                entry = ldap_first_entry(ld, res);
458
 
                if (entry) {
459
 
                        userdn = ldap_get_dn(ld, entry);
460
 
                }
461
 
 
462
 
                ldap_msgfree(res);
463
 
                res = NULL;
464
 
        }
465
 
 
466
 
        ldap_value_free_len(ncvals);
467
 
 
468
 
        if (ret != LDAP_SUCCESS) {
469
 
                syslog(LOG_ERR, "Search for %s failed with error %d",
470
 
                        filter, ret);
471
 
                if (ret == LDAP_CONSTRAINT_VIOLATION) {
472
 
                        *errstr = strdup("Password Change Failed");
473
 
                        kpwd_err = KRB5_KPASSWD_SOFTERROR;
474
 
                }
475
 
                goto done;
476
 
        }
477
 
 
478
 
        free(filter);
479
 
        filter = NULL;
480
 
 
481
 
        if (!userdn) {
482
 
                syslog(LOG_ERR, "No userdn, can't change password!");
483
 
                goto done;
484
 
        }
485
 
 
486
 
        /* build password change control */
487
 
        ctrl = ber_alloc_t(LBER_USE_DER);
488
 
        if (!ctrl) {
489
 
                syslog(LOG_ERR, "Out of memory!");
490
 
                goto done;
491
 
        }
492
 
 
493
 
        ret = ber_printf(ctrl, "{tstO}",
494
 
                   LDAP_TAG_EXOP_MODIFY_PASSWD_ID, userdn,
495
 
                   LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, &newpw);
496
 
        if (ret < 0) {
497
 
                syslog(LOG_ERR, "ber printf failed!");
498
 
                goto done;
499
 
        }
500
 
 
501
 
        ret = ber_flatten(ctrl, &control);
502
 
        if (ret < 0) {
503
 
                syslog(LOG_ERR, "ber flattening failed!");
504
 
                goto done;
505
 
        }
506
 
 
507
 
        /* perform password change */
508
 
        ret = ldap_extended_operation(ld,
509
 
                                        LDAP_EXOP_MODIFY_PASSWD,
510
 
                                        control, NULL, NULL,
511
 
                                        &msgid);
512
 
        if (ret != LDAP_SUCCESS) {
513
 
                syslog(LOG_ERR, "ldap_extended_operation() failed. (%d)", ret);
514
 
                goto done;
515
 
        }
516
 
 
517
 
        tv.tv_sec = 10;
518
 
        tv.tv_usec = 0; 
519
 
 
520
 
        ret = ldap_result(ld, msgid, 1, &tv, &res);
521
 
        if (ret == -1) {
522
 
                ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &rc);
523
 
                syslog(LOG_ERR, "ldap_result() failed. (%d)", rc);
524
 
                goto done;
525
 
        }
526
 
 
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.");
530
 
                ldap_msgfree(res);
531
 
                goto done;
532
 
        }
533
 
        if (retoid || retdata) {
534
 
                syslog(LOG_ERR, "ldap_parse_extended_result() returned data, but we don't handle it yet.");
535
 
        }
536
 
 
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.");
540
 
                goto done;
541
 
        }
542
 
        if (rc != LDAP_SUCCESS) {
543
 
                if (rc == LDAP_CONSTRAINT_VIOLATION) {
544
 
                        kpwd_err = KRB5_KPASSWD_SOFTERROR;
545
 
                }
546
 
                ret = LDAP_OPERATIONS_ERROR;
547
 
        }
548
 
        if (err) {
549
 
                syslog(LOG_ERR, "ldap_parse_result(): [%s]", err);
550
 
                ldap_memfree(err);
551
 
        }
552
 
 
553
 
        if (srvctrl) {
554
 
 
555
 
                LDAPControl *pprc = NULL;
556
 
                int i;
557
 
 
558
 
                for (i = 0; srvctrl[i]; i++) {
559
 
                        if (0 == strcmp(srvctrl[i]->ldctl_oid, LDAP_CONTROL_PASSWORDPOLICYRESPONSE)) {
560
 
                                pprc = srvctrl[i];
561
 
                        }
562
 
                }
563
 
                if (pprc) {
564
 
                        sctrl = ber_init(&pprc->ldctl_value);
565
 
                }
566
 
 
567
 
                if (sctrl) {
568
 
                        /*
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),
575
 
                         *              accountLocked         (1),
576
 
                         *              changeAfterReset      (2),
577
 
                         *              passwordModNotAllowed (3),
578
 
                         *              mustSupplyOldPassword (4),
579
 
                         *              invalidPasswordSyntax (5),
580
 
                         *              passwordTooShort      (6),
581
 
                         *              passwordTooYoung      (7),
582
 
                         *              passwordInHistory     (8) } }
583
 
                         */
584
 
 
585
 
                        ber_tag_t rtag, btag;
586
 
                        ber_int_t bint;
587
 
                        rtag = ber_scanf(sctrl, "{t", &btag);
588
 
                        if (rtag == LBER_ERROR) {
589
 
                                syslog(LOG_ERR, "Could not decode the tag BER element");
590
 
                                goto done;
591
 
                        }
592
 
 
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");
597
 
                                        goto done;
598
 
                                }
599
 
                                if (btag == LDAP_TAG_PWP_SECSLEFT) {
600
 
                                        ret = asprintf(&exterr2, " (%d seconds left before password expires)", bint);
601
 
                                } else {
602
 
                                        ret = asprintf(&exterr2, " (%d grace logins remaining)", bint);
603
 
                                }
604
 
                                if (ret == -1) {
605
 
                                        syslog(LOG_ERR, "OOM while creating error message ...");
606
 
                                        exterr2 = NULL;
607
 
                                }
608
 
                                /* The next element might or might not be there (the control is a sequence) */
609
 
                                ber_scanf(sctrl, "t", &btag);
610
 
                        }
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");
615
 
                                        goto done;
616
 
                                }
617
 
 
618
 
                                switch(bint) {
619
 
                                case 0:
620
 
                                        ret = asprintf(&exterr1, " Err%d: Password Expired.", bint);
621
 
                                        break;
622
 
                                case 1:
623
 
                                        ret = asprintf(&exterr1, " Err%d: Account locked.", bint);
624
 
                                        break;
625
 
                                case 2:
626
 
                                        ret = asprintf(&exterr1, " Err%d: Password changed after reset.", bint);
627
 
                                        break;
628
 
                                case 3:
629
 
                                        ret = asprintf(&exterr1, " Err%d: Password change not allowed.", bint);
630
 
                                        break;
631
 
                                case 4:
632
 
                                        ret = asprintf(&exterr1, " Err%d: [Shouldn't happen].", bint);
633
 
                                        break;
634
 
                                case 5:
635
 
                                        ret = asprintf(&exterr1, " Err%d: Password too simple.", bint);
636
 
                                        break;
637
 
                                case 6:
638
 
                                        ret = asprintf(&exterr1, " Err%d: Password too short.", bint);
639
 
                                        break;
640
 
                                case 7:
641
 
                                        ret = asprintf(&exterr1, " Err%d: Too soon to change password.", bint);
642
 
                                        break;
643
 
                                case 8:
644
 
                                        ret = asprintf(&exterr1, " Err%d: Password reuse not permitted.", bint);
645
 
                                        break;
646
 
                                default:
647
 
                                        ret = asprintf(&exterr1, " Err%d: Unknown Errorcode.", bint);
648
 
                                        break;
649
 
                                }
650
 
                                if (ret == -1) {
651
 
                                        syslog(LOG_ERR, "OOM while creating error message ...");
652
 
                                        exterr1 = NULL;
653
 
                                }
654
 
                        }
655
 
                }
656
 
        }
657
 
 
658
 
        if (ret == LDAP_SUCCESS) {
659
 
                kpwd_err = KRB5_KPASSWD_SUCCESS;
660
 
                exterr0 = "Password change succeeded";
661
 
        } else {
662
 
                exterr0 = "Password change failed";
663
 
        }
664
 
        ret = asprintf(errstr, "%s%s%s", exterr0, exterr1?exterr1:"", exterr2?exterr2:"");
665
 
        if (ret == -1) {
666
 
                syslog(LOG_ERR, "OOM while creating error message ...");
667
 
                *errstr = NULL;
668
 
        }
669
 
 
670
 
done:
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);
676
 
        free(exterr1);
677
 
        free(exterr2);
678
 
        free(filter);
679
 
        free(userdn);
680
 
        if (ld) ldap_unbind_ext(ld, NULL, NULL);
681
 
        if (tmp_file) {
682
 
                unlink(tmp_file);
683
 
                free(tmp_file);
684
 
        }
685
 
        return kpwd_err;
686
 
}
687
 
 
688
 
void handle_krb_packets(uint8_t *buf, ssize_t buflen,
689
 
                        struct socklist *sd,
690
 
                        struct sockaddr_storage *from,
691
 
                        uint8_t **repbuf, ssize_t *replen)
692
 
{
693
 
        krb5_auth_context auth_context;
694
 
        krb5_context context;
695
 
        krb5_keytab keytab;
696
 
        krb5_principal kprincpw;
697
 
        krb5_ticket *ticket;
698
 
        krb5_address lkaddr, rkaddr;
699
 
        krb5_data kreq, krep, kenc, kdec;
700
 
        krb5_replay_data replay;
701
 
        krb5_error krb5err;
702
 
        int krberr;
703
 
        size_t reqlen;
704
 
        size_t verno;
705
 
        char *client_name, *realm_name;
706
 
        char *result_string;
707
 
        int result_err;
708
 
        uint8_t *reply;
709
 
        ssize_t replylen;
710
 
 
711
 
        *replen = 0;
712
 
 
713
 
        result_string = NULL;
714
 
        auth_context = NULL;
715
 
        krep.length = 0;
716
 
        krep.data = NULL;
717
 
        kdec.length = 0;
718
 
        kdec.data = NULL;
719
 
        kprincpw = NULL;
720
 
        context = NULL;
721
 
        ticket = NULL;
722
 
 
723
 
        switch(((struct sockaddr *)from)->sa_family) {
724
 
        case AF_INET:
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);
728
 
 
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);
732
 
                break;
733
 
        case AF_INET6:
734
 
                if (IN6_IS_ADDR_V4MAPPED (&((struct sockaddr_in6 *)from)->sin6_addr)) {
735
 
                        lkaddr.addrtype = ADDRTYPE_INET;
736
 
                        lkaddr.length = 4;
737
 
                        lkaddr.contents = 12 + (krb5_octet *) &(((struct sockaddr_in6 *)&sd->dest_addr)->sin6_addr);
738
 
 
739
 
                        rkaddr.addrtype = ADDRTYPE_INET;
740
 
                        rkaddr.length = 4;
741
 
                        rkaddr.contents = 12 + (krb5_octet *) &(((struct sockaddr_in6 *)from)->sin6_addr);
742
 
                } else {
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);
746
 
 
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);
750
 
                }
751
 
                break;
752
 
        default:
753
 
                result_string = strdup("Invalid remopte IP address");
754
 
                result_err = KRB5_KPASSWD_MALFORMED;
755
 
                syslog(LOG_ERR, "%s", result_string);
756
 
                goto done;
757
 
        }
758
 
 
759
 
        if (buflen < 4) {
760
 
                result_string = strdup("Request truncated");
761
 
                result_err = KRB5_KPASSWD_MALFORMED;
762
 
                syslog(LOG_ERR, "%s", result_string);
763
 
                goto done;
764
 
        }
765
 
 
766
 
        reqlen = (buf[0] << 8) + buf[1];
767
 
 
768
 
        if (reqlen != buflen) {
769
 
                result_string = strdup("Unmatching request length");
770
 
                result_err = KRB5_KPASSWD_MALFORMED;
771
 
                syslog(LOG_ERR, "%s", result_string);
772
 
                goto done;
773
 
        }
774
 
 
775
 
        verno = (buf[2] << 8) + buf[3];
776
 
 
777
 
        if (verno != 1) {
778
 
                result_string = strdup("Unsupported version");
779
 
                result_err = KRB5_KPASSWD_BAD_VERSION;
780
 
                syslog(LOG_ERR, "%s", result_string);
781
 
                goto done;
782
 
        }
783
 
 
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);
789
 
                goto done;
790
 
        }
791
 
        kreq.data = (char *)&buf[6];
792
 
 
793
 
        krberr = krb5_init_context(&context);
794
 
        if (krberr) {
795
 
                result_string = strdup("Failed to init kerberos context");
796
 
                result_err = KRB5_KPASSWD_HARDERROR;
797
 
                syslog(LOG_ERR, "%s", result_string);
798
 
                goto done;
799
 
        }
800
 
 
801
 
        krberr = krb5_get_default_realm(context, &realm_name);
802
 
        if (krberr) {
803
 
                result_string = strdup("Failed to get default realm name");
804
 
                result_err = KRB5_KPASSWD_HARDERROR;
805
 
                syslog(LOG_ERR, "%s", result_string);
806
 
                goto done;
807
 
        }
808
 
 
809
 
        krberr = krb5_auth_con_init(context, &auth_context);
810
 
        if (krberr) {
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));
815
 
                goto done;
816
 
        }
817
 
 
818
 
        krberr = krb5_auth_con_setflags(context, auth_context,
819
 
                                        KRB5_AUTH_CONTEXT_DO_SEQUENCE);
820
 
        if (krberr) {
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));
825
 
                goto done;
826
 
        }
827
 
 
828
 
        krberr = krb5_build_principal(context, &kprincpw,
829
 
                                      strlen(realm_name), realm_name,
830
 
                                      "kadmin", "changepw", NULL);
831
 
        if (krberr) {
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));
836
 
                goto done;
837
 
        }
838
 
 
839
 
        krberr = krb5_kt_resolve(context, keytab_name, &keytab);
840
 
        if (krberr) {
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));
845
 
                goto done;
846
 
        }
847
 
 
848
 
        krberr = krb5_rd_req(context, &auth_context, &kreq,
849
 
                             kprincpw, keytab, NULL, &ticket);
850
 
        if (krberr) {
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));
855
 
                goto done;
856
 
        }
857
 
 
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);
862
 
        if (krberr) {
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));
867
 
                goto done;
868
 
        }
869
 
 
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);
875
 
                goto kpreply;
876
 
        }
877
 
 
878
 
        krberr = krb5_unparse_name(context, ticket->enc_part2->client,
879
 
                                   &client_name);
880
 
        if (krberr) {
881
 
                result_string = strdup("Unable to parse client name");
882
 
                result_err = KRB5_KPASSWD_HARDERROR;
883
 
                syslog(LOG_ERR, "%s", result_string);
884
 
                goto kpreply;
885
 
        }
886
 
 
887
 
        krberr = krb5_auth_con_setaddrs(context, auth_context, NULL, &rkaddr);
888
 
        if (krberr) {
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));
893
 
                goto kpreply;
894
 
        }
895
 
 
896
 
        /* decrypt the new password */
897
 
        kenc.length = reqlen - kreq.length - 6;
898
 
        kenc.data = kreq.data + kreq.length;
899
 
 
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);
903
 
        if (krberr) {
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));
908
 
                goto kpreply;
909
 
        }
910
 
 
911
 
        if (debug > 100) {
912
 
                syslog(LOG_ERR, "Client %s trying to set password [%*s]",
913
 
                        client_name, kdec.length, kdec.data);
914
 
        }
915
 
 
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");
920
 
        }
921
 
        syslog(LOG_ERR, "%s", result_string);
922
 
 
923
 
        /* make sure password is cleared off before we free the memory */
924
 
        memset(kdec.data, 0, kdec.length);
925
 
        free(kdec.data);
926
 
        kdec.length = 0;
927
 
 
928
 
kpreply:
929
 
 
930
 
        /* set-up the clear text reply */
931
 
        kdec.length = 2 + strlen(result_string);
932
 
        kdec.data = malloc(kdec.length);
933
 
        if (!kdec.data) {
934
 
                syslog(LOG_ERR, "Out of memory!");
935
 
                kdec.length = 0;
936
 
                goto done;
937
 
        }
938
 
        
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));
942
 
 
943
 
        free(result_string);
944
 
        result_string = NULL;
945
 
 
946
 
        krberr = krb5_auth_con_setaddrs(context, auth_context, &lkaddr, NULL);
947
 
        if (krberr) {
948
 
                result_string = strdup("Failed to set local address");
949
 
                syslog(LOG_ERR, "%s: %s", result_string, 
950
 
                        krb5_get_error_message(context, krberr));
951
 
                goto done;
952
 
        }
953
 
 
954
 
        krberr = krb5_mk_priv(context, auth_context, &kdec, &kenc, &replay);
955
 
        if (krberr) {
956
 
                result_string = strdup("Failed to encrypt reply message");
957
 
                syslog(LOG_ERR, "%s: %s", result_string, 
958
 
                        krb5_get_error_message(context, krberr));
959
 
 
960
 
                free(result_string);
961
 
                result_string = NULL;
962
 
                /* encryption was unsuccessful, let's return a krb error */
963
 
 
964
 
                /* the ap data is no more useful */
965
 
                free(krep.data);
966
 
                krep.length = 0;
967
 
 
968
 
                /* build a krberror encrypted paylod */
969
 
                krb5err.error = KRB5_CHPW_FAIL;
970
 
                krb5err.server = kprincpw;
971
 
                krb5err.client = NULL;
972
 
                krb5err.ctime = 0;
973
 
                krb5err.cusec = 0;
974
 
                krb5err.susec = 0;
975
 
                krberr = krb5_timeofday(context, &krb5err.stime);
976
 
                if (krberr) {
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));
980
 
                        goto done;
981
 
                }
982
 
 
983
 
                krb5err.text.length = 0;
984
 
                krb5err.e_data = kdec;
985
 
                krberr = krb5_mk_error(context, &krb5err, &kenc);
986
 
                if (krberr) {
987
 
                        result_string = strdup("Failed to build error message");
988
 
                        syslog(LOG_ERR, "%s: %s", result_string, 
989
 
                                krb5_get_error_message(context, krberr));
990
 
                        goto done;
991
 
                }
992
 
        }
993
 
 
994
 
        replylen = 6 + krep.length + kenc.length;
995
 
        reply = malloc(replylen);
996
 
        if (!reply) {
997
 
                syslog(LOG_ERR, "Out of memory!");
998
 
                goto done;
999
 
        }
1000
 
        *repbuf = reply;
1001
 
 
1002
 
        reply[0] = (replylen >> 8) & 0xff;
1003
 
        reply[1] = replylen & 0xff;
1004
 
        reply[2] = 0x00;
1005
 
        reply[3] = 0x01;
1006
 
        reply[4] = (krep.length >> 8) & 0xff;
1007
 
        reply[5] = krep.length & 0xff;
1008
 
 
1009
 
        if (krep.length) {
1010
 
                memcpy(&reply[6], krep.data, krep.length);
1011
 
        }
1012
 
        memcpy(&reply[6 + krep.length], kenc.data, kenc.length);
1013
 
 
1014
 
        *replen = replylen;
1015
 
 
1016
 
done:
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);
1024
 
}
1025
 
 
1026
 
pid_t handle_conn(struct socklist *sd)
1027
 
{
1028
 
        int mfd, tcp;
1029
 
        pid_t pid;
1030
 
        char addrto6[INET6_ADDRSTRLEN+1];
1031
 
        char address[INET6_ADDRSTRLEN+1];
1032
 
        uint8_t request[1500];
1033
 
        ssize_t reqlen;
1034
 
        uint8_t *reply;
1035
 
        ssize_t replen;
1036
 
        struct sockaddr_storage from;
1037
 
        socklen_t fromlen;
1038
 
        ssize_t sendret;
1039
 
        int ret;
1040
 
 
1041
 
        fromlen = sizeof(from);
1042
 
        mfd = 0;
1043
 
        tcp = 0;
1044
 
        reqlen = 0;
1045
 
 
1046
 
        /* receive request */
1047
 
        if (sd->socktype == SOCK_STREAM) {
1048
 
                tcp = 1;
1049
 
                mfd = accept(sd->fd, (struct sockaddr *)&from, &fromlen);
1050
 
                if (mfd == -1) {
1051
 
                        syslog(LOG_ERR, "Accept failed with error (%d) %s",
1052
 
                                errno, strerror(errno));
1053
 
                        return -1;
1054
 
                }
1055
 
        } else {
1056
 
                /* read first to empty the buffer on udp connections */
1057
 
                reqlen = recvfrom(sd->fd, request, sizeof(request), 0,
1058
 
                                   (struct sockaddr *)&from, &fromlen);
1059
 
                if (reqlen <= 0) {
1060
 
                        syslog(LOG_ERR, "Error receiving request (%d) %s",
1061
 
                                errno, strerror(errno));
1062
 
                        return -1;
1063
 
                }
1064
 
 
1065
 
        }
1066
 
 
1067
 
        ret = getnameinfo((struct sockaddr *)&from, fromlen,
1068
 
                          addrto6, INET6_ADDRSTRLEN+1,
1069
 
                          NULL, 0, NI_NUMERICHOST);
1070
 
        if (ret) {
1071
 
                syslog(LOG_ERR, "Error retrieving host address\n");
1072
 
                return -1;
1073
 
        }
1074
 
 
1075
 
        if (debug > 0) {
1076
 
                syslog(LOG_ERR, "Connection from %s", addrto6);
1077
 
        }
1078
 
 
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);
1085
 
        } else {
1086
 
                /* regular IPv6 address, copy as is */
1087
 
                memcpy(address, addrto6, INET6_ADDRSTRLEN);
1088
 
        }
1089
 
        /* make sure we have termination */
1090
 
        address[INET6_ADDRSTRLEN] = '\0';
1091
 
 
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 */
1097
 
 
1098
 
        if (check_blacklist(address)) {
1099
 
                if (debug > 0) {
1100
 
                        syslog(LOG_ERR, "[%s] blacklisted", address);
1101
 
                }
1102
 
                if (tcp) close(mfd);
1103
 
                return 0;
1104
 
        }
1105
 
 
1106
 
        /* now read data if it was a TCP connection */
1107
 
        if (tcp) {
1108
 
                reqlen = recvfrom(mfd, request, sizeof(request), 0,
1109
 
                                   (struct sockaddr *)&from, &fromlen);
1110
 
                if (reqlen <= 0) {
1111
 
                        syslog(LOG_ERR, "Error receiving request (%d) %s",
1112
 
                                errno, strerror(errno));
1113
 
                        close(mfd);
1114
 
                        return -1;
1115
 
                }
1116
 
        }
1117
 
#if 1
1118
 
        /* handle kerberos and ldap operations in childrens */
1119
 
        pid = fork();
1120
 
        if (pid == -1) {
1121
 
                syslog(LOG_ERR, "Fork failed with error (%d) %s",
1122
 
                        errno, strerror(errno));
1123
 
                if (tcp) close(mfd);
1124
 
                return 0;
1125
 
        }
1126
 
        if (pid != 0) { /* parent */
1127
 
                if (tcp) close(mfd);
1128
 
                add_blacklist(pid, address);
1129
 
                return pid;
1130
 
        }
1131
 
#endif
1132
 
 
1133
 
        /* children */
1134
 
        if (debug > 0) syslog(LOG_ERR, "Servicing %s", address);
1135
 
 
1136
 
        /* TCP packets prepend the lenght as a 32bit network order field,
1137
 
         * this information seem to be just redundant, so let's simply
1138
 
         * skip it */
1139
 
        if (tcp) {
1140
 
                handle_krb_packets(request+4, reqlen-4, sd, &from, &reply, &replen);
1141
 
        } else {
1142
 
                handle_krb_packets(request, reqlen, sd, &from, &reply, &replen);
1143
 
        }
1144
 
 
1145
 
        if (replen) { /* we have something to reply */
1146
 
                if (tcp) {
1147
 
                        sendret = sendto(mfd, reply, replen, 0, NULL, 0);
1148
 
                } else {
1149
 
                        sendret = sendto(sd->fd, reply, replen, 0, (struct sockaddr *)&from, fromlen);
1150
 
                }
1151
 
                if (sendret == -1) {
1152
 
                        syslog(LOG_ERR, "Error sending reply (%d)", errno);
1153
 
                }
1154
 
        }
1155
 
        if (tcp) close(mfd);
1156
 
        exit(0);
1157
 
}
1158
 
 
1159
 
static int create_socket(struct addrinfo *ai, struct socklist **_sds,
1160
 
                         struct pollfd **_pfds, int *_nfds)
1161
 
{
1162
 
        struct socklist *csd, *tsd;
1163
 
        struct pollfd *pfds;
1164
 
        int nfds;
1165
 
        int ret;
1166
 
        int tru = 1;
1167
 
 
1168
 
        pfds = *_pfds;
1169
 
        nfds = *_nfds;
1170
 
 
1171
 
        csd = calloc(1, sizeof(struct socklist));
1172
 
        if (csd == NULL) {
1173
 
                syslog(LOG_ERR, "Out of memory, can't create socklist\n");
1174
 
                return 1;
1175
 
        }
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);
1179
 
 
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)",
1183
 
                       strerror(errno));
1184
 
                goto errout;
1185
 
        }
1186
 
        ret = setsockopt(csd->fd, SOL_SOCKET, SO_REUSEADDR,
1187
 
                         (void *)&tru, sizeof(tru));
1188
 
 
1189
 
        ret = bind(csd->fd, (struct sockaddr *)&csd->dest_addr, csd->dest_addr_len);
1190
 
        if (ret) {
1191
 
                if (errno != EADDRINUSE) {
1192
 
                        syslog(LOG_ERR, "Unable to bind to socket");
1193
 
                        close(csd->fd);
1194
 
                        goto errout;
1195
 
                }
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
1200
 
                 * Just ignore */
1201
 
                close(csd->fd);
1202
 
                free(csd);
1203
 
                return 0;
1204
 
        }
1205
 
 
1206
 
        if (csd->socktype == SOCK_STREAM) {
1207
 
                ret = listen(csd->fd, SOMAXCONN);
1208
 
                if (ret) {
1209
 
                        syslog(LOG_ERR, "Unable to listen to TCP socket (%s)",
1210
 
                               strerror(errno));
1211
 
                        close(csd->fd);
1212
 
                        goto errout;
1213
 
                }
1214
 
        }
1215
 
 
1216
 
        pfds = realloc(pfds, sizeof(struct pollfd) * (nfds +1));
1217
 
        if (pfds == NULL) {
1218
 
                syslog(LOG_ERR, "Out of memory, can't alloc pollfd array\n");
1219
 
                close(csd->fd);
1220
 
                goto errout;
1221
 
        }
1222
 
        pfds[nfds].events = POLLIN;
1223
 
        pfds[nfds].fd = csd->fd;
1224
 
        nfds++;
1225
 
 
1226
 
        if (*_sds) {
1227
 
                for (tsd = *_sds; tsd->next; tsd = tsd->next) /* skip */ ;
1228
 
                tsd->next = csd;
1229
 
        } else {
1230
 
                *_sds = csd;
1231
 
        }
1232
 
 
1233
 
        *_pfds = pfds;
1234
 
        *_nfds = nfds;
1235
 
 
1236
 
        return 0;
1237
 
 
1238
 
errout:
1239
 
        free(csd);
1240
 
        return 1;
1241
 
}
1242
 
 
1243
 
int main(int argc, char *argv[])
1244
 
{
1245
 
        pid_t pid;
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;
1252
 
        int nfds;
1253
 
        int ret;
1254
 
        char *env;
1255
 
 
1256
 
        /* log to syslog */
1257
 
        openlog("kpasswd", LOG_PID, LOG_DAEMON);
1258
 
 
1259
 
        /* do not keep any fs busy */
1260
 
        ret = chdir("/");
1261
 
        if (ret == -1) {
1262
 
                syslog(LOG_ERR, "Unable to change dir to '/'");
1263
 
                exit(-1);
1264
 
        }
1265
 
 
1266
 
        /* daemonize */
1267
 
        pid = fork();
1268
 
        if (pid == -1) {
1269
 
                syslog(LOG_ERR, "Error fork() failed!");
1270
 
                exit(-1);
1271
 
        }
1272
 
        if (pid != 0) { /* parent */
1273
 
                exit(0);
1274
 
        }
1275
 
 
1276
 
        /* new session */
1277
 
        setsid();
1278
 
 
1279
 
        /* close std* descriptors */
1280
 
        close(0);
1281
 
        close(1);
1282
 
        close(2);
1283
 
 
1284
 
        /* fork again to make sure we completely detach from parent process */
1285
 
        pid = fork();
1286
 
        if (pid == -1) {
1287
 
                syslog(LOG_ERR, "Error fork() failed!");
1288
 
                exit(-1);
1289
 
        }
1290
 
        if (pid != 0) { /* parent */
1291
 
                exit(0);
1292
 
        }
1293
 
 
1294
 
        /* source env vars */
1295
 
        env = getenv("KRB5_KTNAME");
1296
 
        if (!env) {
1297
 
                env = DEFAULT_KEYTAB;
1298
 
        }
1299
 
        keytab_name = strdup(env);
1300
 
        if (!keytab_name) {
1301
 
                syslog(LOG_ERR, "Out of memory!");
1302
 
        }
1303
 
 
1304
 
        env = getenv("IPA_KPASSWD_DEBUG");
1305
 
        if (env) {
1306
 
                debug = strtol(env, NULL, 0);
1307
 
        }
1308
 
 
1309
 
        ret = getifaddrs(&ifa);
1310
 
        if (ret) {
1311
 
                syslog(LOG_ERR, "getifaddrs failed: %s", gai_strerror(ret));
1312
 
                exit(1);
1313
 
        }
1314
 
 
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");
1318
 
        int fail = 1;
1319
 
        if (f) {
1320
 
                int n_bytes = fprintf(f, "%ld\n", (long) getpid());
1321
 
                if (fclose(f) == 0 && 0 < n_bytes)
1322
 
                        fail = 0;
1323
 
        }
1324
 
        if (fail) {
1325
 
                syslog(LOG_ERR, "Couldn't create pid file %s: %s",
1326
 
                       pid_file, strerror(errno));
1327
 
                exit(1);
1328
 
        }
1329
 
 
1330
 
        nfds = 0;
1331
 
        pfds = NULL;
1332
 
        sds = NULL;
1333
 
 
1334
 
        for (tifa = ifa; tifa; tifa = tifa->ifa_next) {
1335
 
 
1336
 
                if (NULL == tifa->ifa_addr)
1337
 
                        /* uhmm no address ?? skip it */
1338
 
                        continue;
1339
 
 
1340
 
                if (tifa->ifa_addr->sa_family != AF_INET &&
1341
 
                    tifa->ifa_addr->sa_family != AF_INET6) {
1342
 
                        /* not interesting for us */
1343
 
                        continue;
1344
 
                }
1345
 
 
1346
 
                ret = getnameinfo(tifa->ifa_addr, sizeof(struct sockaddr_storage),
1347
 
                                  host, sizeof(host), NULL, 0, NI_NUMERICHOST);
1348
 
                if (ret) {
1349
 
                        syslog(LOG_ERR, "Error converting address (%s)",
1350
 
                                gai_strerror(ret));
1351
 
                        continue;
1352
 
                } else {
1353
 
                        syslog(LOG_INFO, "Setting up socket for [%s]", host);
1354
 
                }
1355
 
 
1356
 
                memset(&hints, 0, sizeof(hints));
1357
 
                hints.ai_flags = AI_NUMERICHOST;
1358
 
                hints.ai_family = AF_UNSPEC;
1359
 
 
1360
 
                /* this should return 2 entries, one for UDP and one for TCP */
1361
 
                ret = getaddrinfo(host, "kpasswd", &hints, &ai);
1362
 
                if (ret) {
1363
 
                        syslog(LOG_ERR, "Error getting address info (%s) for [%s]",
1364
 
                                gai_strerror(ret), host);
1365
 
                        continue;
1366
 
                }
1367
 
 
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);
1371
 
                        if (ret) {
1372
 
                                syslog(LOG_ERR,
1373
 
                                       "Failed to set up %s socket for [%s]",
1374
 
                                       socktype, host);
1375
 
                        }
1376
 
                }
1377
 
        }
1378
 
 
1379
 
        if (nfds == 0) {
1380
 
                syslog(LOG_ERR, "Failed to setup any socket. Aborting");
1381
 
                exit(1);
1382
 
        }
1383
 
 
1384
 
        /* now that sockets are set up, enter the poll loop */
1385
 
 
1386
 
        while (1) {
1387
 
                int cstatus, cid, i;
1388
 
 
1389
 
                ret = poll(pfds, nfds, 3000);
1390
 
 
1391
 
                switch(ret) {
1392
 
                case 0:
1393
 
                        break;
1394
 
                case -1:
1395
 
                        if (errno != EINTR) {
1396
 
                                syslog(LOG_ERR,
1397
 
                                        "Unexpected error in poll (%d) %s",
1398
 
                                        errno, strerror(errno));
1399
 
                                exit(5);
1400
 
                        }
1401
 
                        break;
1402
 
                default:
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) {
1407
 
                                                        handle_conn(csd);
1408
 
                                                }
1409
 
                                        }
1410
 
                                }
1411
 
                        }
1412
 
                }
1413
 
 
1414
 
                /* check for children exiting */
1415
 
                cid = waitpid(-1, &cstatus, WNOHANG);
1416
 
                if (cid != -1 && cid != 0) {
1417
 
                        if (debug > 0)
1418
 
                                syslog(LOG_ERR, "pid %d completed operations!\n", cid);
1419
 
                        remove_blacklist(cid);
1420
 
                }
1421
 
        }
1422
 
}