1    	/*
2    	    SSSD
3    	
4    	    Kerberos 5 Backend Module -- tgt_req and changepw child
5    	
6    	    Authors:
7    	        Sumit Bose <sbose@redhat.com>
8    	
9    	    Copyright (C) 2009-2010 Red Hat
10   	
11   	    This program is free software; you can redistribute it and/or modify
12   	    it under the terms of the GNU General Public License as published by
13   	    the Free Software Foundation; either version 3 of the License, or
14   	    (at your option) any later version.
15   	
16   	    This program is distributed in the hope that it will be useful,
17   	    but WITHOUT ANY WARRANTY; without even the implied warranty of
18   	    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   	    GNU General Public License for more details.
20   	
21   	    You should have received a copy of the GNU General Public License
22   	    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   	*/
24   	
25   	#include <sys/types.h>
26   	#include <unistd.h>
27   	#include <sys/stat.h>
28   	#include <popt.h>
29   	
30   	#include <security/pam_modules.h>
31   	
32   	#include "util/util.h"
33   	#include "util/user_info_msg.h"
34   	#include "providers/child_common.h"
35   	#include "providers/dp_backend.h"
36   	#include "providers/krb5/krb5_auth.h"
37   	#include "providers/krb5/krb5_utils.h"
38   	
39   	struct krb5_child_ctx {
40   	    /* opts taken from kinit */
41   	    /* in seconds */
42   	    krb5_deltat starttime;
43   	    krb5_deltat lifetime;
44   	    krb5_deltat rlife;
45   	
46   	    int forwardable;
47   	    int proxiable;
48   	    int addresses;
49   	
50   	    int not_forwardable;
51   	    int not_proxiable;
52   	    int no_addresses;
53   	
54   	    int verbose;
55   	
56   	    char* principal_name;
57   	    char* service_name;
58   	    char* keytab_name;
59   	    char* k5_cache_name;
60   	    char* k4_cache_name;
61   	
62   	    action_type action;
63   	
64   	    char *kdcip;
65   	    char *realm;
66   	    char *changepw_principle;
67   	    char *ccache_dir;
68   	    char *ccname_template;
69   	    int auth_timeout;
70   	
71   	    int child_debug_fd;
72   	};
73   	
74   	struct krb5_req {
75   	    krb5_context ctx;
76   	    krb5_principal princ;
77   	    char* name;
78   	    krb5_creds *creds;
79   	    krb5_get_init_creds_opt *options;
80   	    pid_t child_pid;
81   	    int read_from_child_fd;
82   	    int write_to_child_fd;
83   	
84   	    struct be_req *req;
85   	    struct pam_data *pd;
86   	    struct krb5_child_ctx *krb5_ctx;
87   	    errno_t (*child_req)(int fd, struct krb5_req *kr);
88   	
89   	    char *ccname;
90   	    char *keytab;
91   	    bool validate;
92   	
93   	    const char *upn;
94   	    uid_t uid;
95   	    gid_t gid;
96   	};
97   	
98   	static krb5_context krb5_error_ctx;
99   	static const char *__krb5_error_msg;
100  	#define KRB5_DEBUG(level, krb5_error) do { \
101  	    __krb5_error_msg = sss_krb5_get_error_message(krb5_error_ctx, krb5_error); \
102  	    DEBUG(level, ("%d: [%d][%s]\n", __LINE__, krb5_error, __krb5_error_msg)); \
103  	    sss_krb5_free_error_message(krb5_error_ctx, __krb5_error_msg); \
104  	} while(0);
105  	
106  	
107  	static krb5_error_code sss_krb5_prompter(krb5_context context, void *data,
108  	                                         const char *name, const char *banner,
109  	                                         int num_prompts, krb5_prompt prompts[])
110  	{
111  	    int ret;
112  	    struct krb5_req *kr = talloc_get_type(data, struct krb5_req);
113  	
114  	    if (num_prompts != 0) {
115  	        DEBUG(1, ("Cannot handle password prompts.\n"));
116  	        return KRB5_LIBOS_CANTREADPWD;
117  	    }
118  	
119  	    if (banner == NULL || *banner == '\0') {
120  	        DEBUG(5, ("Prompter called with empty banner, nothing to do.\n"));
121  	        return EOK;
122  	    }
123  	
124  	    DEBUG(9, ("Prompter called with [%s].\n", banner));
125  	
126  	    ret = pam_add_response(kr->pd, SSS_PAM_TEXT_MSG, strlen(banner)+1,
127  	                           (const uint8_t *) banner);
128  	    if (ret != EOK) {
129  	        DEBUG(1, ("pam_add_response failed.\n"));
130  	    }
131  	
132  	    return EOK;
133  	}
134  	
135  	
136  	static krb5_error_code create_empty_cred(struct krb5_req *kr, krb5_creds **_cred)
137  	{
138  	    krb5_error_code kerr;
139  	    krb5_creds *cred = NULL;
140  	    krb5_data *krb5_realm;
141  	
142  	    cred = calloc(sizeof(krb5_creds), 1);
143  	    if (cred == NULL) {
144  	        DEBUG(1, ("calloc failed.\n"));
145  	        return ENOMEM;
146  	    }
147  	
148  	    kerr = krb5_copy_principal(kr->ctx, kr->princ, &cred->client);
149  	    if (kerr != 0) {
150  	        DEBUG(1, ("krb5_copy_principal failed.\n"));
151  	        goto done;
152  	    }
153  	
154  	    krb5_realm = krb5_princ_realm(kr->ctx, kr->princ);
155  	
156  	    kerr = krb5_build_principal_ext(kr->ctx, &cred->server,
157  	                                    krb5_realm->length, krb5_realm->data,
158  	                                    KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
159  	                                    krb5_realm->length, krb5_realm->data, 0);
160  	    if (kerr != 0) {
161  	        DEBUG(1, ("krb5_build_principal_ext failed.\n"));
162  	        goto done;
163  	    }
164  	
165  	done:
166  	    if (kerr != 0) {
167  	        if (cred != NULL && cred->client != NULL) {
168  	            krb5_free_principal(kr->ctx, cred->client);
169  	        }
170  	
171  	        free(cred);
172  	    } else {
173  	        *_cred = cred;
174  	    }
175  	
176  	    return kerr;
177  	}
178  	
179  	static krb5_error_code create_ccache_file(struct krb5_req *kr, krb5_creds *creds)
180  	{
181  	    krb5_error_code kerr;
182  	    krb5_ccache tmp_cc = NULL;
183  	    char *cc_file_name;
184  	    int fd = -1;
185  	    size_t ccname_len;
186  	    char *dummy;
187  	    char *tmp_ccname;
188  	    krb5_creds *l_cred;
189  	
190  	    if (strncmp(kr->ccname, "FILE:", 5) == 0) {
191  	        cc_file_name = kr->ccname + 5;
192  	    } else {
193  	        cc_file_name = kr->ccname;
194  	    }
195  	
196  	    if (cc_file_name[0] != '/') {
197  	        DEBUG(1, ("Ccache filename is not an absolute path.\n"));
198  	        return EINVAL;
199  	    }
200  	
201  	    dummy = strrchr(cc_file_name, '/');
202  	    tmp_ccname = talloc_strndup(kr, cc_file_name, (size_t) (dummy-cc_file_name));
203  	    if (tmp_ccname == NULL) {
204  	        DEBUG(1, ("talloc_strdup failed.\n"));
205  	        return ENOMEM;
206  	    }
207  	    tmp_ccname = talloc_asprintf_append(tmp_ccname, "/.krb5cc_dummy_XXXXXX");
208  	
209  	    fd = mkstemp(tmp_ccname);
210  	    if (fd == -1) {
211  	        DEBUG(1, ("mkstemp failed [%d][%s].\n", errno, strerror(errno)));
212  	        return errno;
213  	    }
214  	
215  	    kerr = krb5_cc_resolve(kr->ctx, tmp_ccname, &tmp_cc);
216  	    if (kerr != 0) {
217  	        KRB5_DEBUG(1, kerr);
218  	        goto done;
219  	    }
220  	
221  	    kerr = krb5_cc_initialize(kr->ctx, tmp_cc, kr->princ);
222  	    if (kerr != 0) {
223  	        KRB5_DEBUG(1, kerr);
224  	        goto done;
225  	    }
226  	    if (fd != -1) {
227  	        close(fd);
228  	        fd = -1;
229  	    }
230  	
231  	    if (creds == NULL) {
232  	        kerr = create_empty_cred(kr, &l_cred);
233  	        if (kerr != 0) {
234  	            KRB5_DEBUG(1, kerr);
235  	            goto done;
236  	        }
237  	    } else {
238  	        l_cred = creds;
239  	    }
240  	
241  	    kerr = krb5_cc_store_cred(kr->ctx, tmp_cc, l_cred);
242  	    if (kerr != 0) {
243  	        KRB5_DEBUG(1, kerr);
244  	        goto done;
245  	    }
246  	
247  	    kerr = krb5_cc_close(kr->ctx, tmp_cc);
248  	    if (kerr != 0) {
249  	        KRB5_DEBUG(1, kerr);
250  	        goto done;
251  	    }
252  	    tmp_cc = NULL;
253  	
254  	    ccname_len = strlen(cc_file_name);
255  	    if (ccname_len >= 6 && strcmp(cc_file_name + (ccname_len-6), "XXXXXX")==0 ) {
256  	        fd = mkstemp(cc_file_name);
257  	        if (fd == -1) {
258  	            DEBUG(1, ("mkstemp failed [%d][%s].\n", errno, strerror(errno)));
259  	            kerr = errno;
260  	            goto done;
261  	        }
262  	    }
263  	
264  	    kerr = rename(tmp_ccname, cc_file_name);
265  	    if (kerr == -1) {
266  	        DEBUG(1, ("rename failed [%d][%s].\n", errno, strerror(errno)));
267  	    }
268  	
269  	done:
270  	    if (fd != -1) {
271  	        close(fd);
272  	        fd = -1;
273  	    }
274  	    if (kerr != 0 && tmp_cc != NULL) {
275  	        krb5_cc_destroy(kr->ctx, tmp_cc);
276  	    }
277  	    return kerr;
278  	}
279  	
280  	static errno_t pack_response_packet(struct response *resp, int status,
281  	                                    struct pam_data *pd)
282  	{
283  	    size_t size = 0;
284  	    size_t p = 0;
285  	    struct response_data *pdr;
286  	
287  	    /* A buffer with the following structure must be created:
288  	     * int32_t status of the request (required)
289  	     * message (zero or more)
290  	     *
291  	     * A message consists of:
292  	     * int32_t type of the message
293  	     * int32_t length of the following data
294  	     * uint8_t[len] data
295  	     */
296  	
297  	    size = sizeof(int32_t);
298  	
299  	    pdr = pd->resp_list;
300  	    while (pdr != NULL) {
301  	        size += 2*sizeof(int32_t) + pdr->len;
302  	        pdr = pdr->next;
303  	    }
304  	
305  	
306  	    resp->buf = talloc_array(resp, uint8_t, size);
307  	    if (!resp->buf) {
308  	        DEBUG(1, ("Insufficient memory to create message.\n"));
309  	        return ENOMEM;
310  	    }
311  	
312  	    SAFEALIGN_SET_INT32(&resp->buf[p], status, &p);
313  	
314  	    pdr = pd->resp_list;
315  	    while(pdr != NULL) {
316  	        SAFEALIGN_SET_INT32(&resp->buf[p], pdr->type, &p);
317  	        SAFEALIGN_SET_INT32(&resp->buf[p], pdr->len, &p);
318  	        safealign_memcpy(&resp->buf[p], pdr->data, pdr->len, &p);
319  	
320  	        pdr = pdr->next;
321  	    }
322  	
323  	
324  	    resp->size = p;
325  	
326  	    return EOK;
327  	}
328  	
329  	static struct response *prepare_response_message(struct krb5_req *kr,
330  	                                                 krb5_error_code kerr,
331  	                                                 int pam_status)
332  	{
333  	    char *msg = NULL;
334  	    const char *krb5_msg = NULL;
335  	    int ret;
336  	    struct response *resp;
337  	
338  	    resp = talloc_zero(kr, struct response);
339  	    if (resp == NULL) {
340  	        DEBUG(1, ("Initializing response failed.\n"));
341  	        return NULL;
342  	    }
343  	
344  	    if (kerr == 0) {
345  	        if(kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) {
346  	            pam_status = PAM_SUCCESS;
347  	            ret = EOK;
348  	        } else {
349  	            if (kr->ccname == NULL) {
350  	                DEBUG(1, ("Error obtaining ccname.\n"));
351  	                return NULL;
352  	            }
353  	
354  	            msg = talloc_asprintf(kr, "%s=%s",CCACHE_ENV_NAME, kr->ccname);
355  	            if (msg == NULL) {
356  	                DEBUG(1, ("talloc_asprintf failed.\n"));
357  	                return NULL;
358  	            }
359  	
360  	            pam_status = PAM_SUCCESS;
361  	            ret = pam_add_response(kr->pd, SSS_PAM_ENV_ITEM, strlen(msg) + 1,
362  	                                   (uint8_t *) msg);
363  	            talloc_zfree(msg);
364  	        }
365  	    } else {
366  	        krb5_msg = sss_krb5_get_error_message(krb5_error_ctx, kerr);
367  	        if (krb5_msg == NULL) {
368  	            DEBUG(1, ("sss_krb5_get_error_message failed.\n"));
369  	            return NULL;
370  	        }
371  	
372  	        ret = pam_add_response(kr->pd, SSS_PAM_SYSTEM_INFO,
373  	                               strlen(krb5_msg) + 1,
374  	                               (const uint8_t *) krb5_msg);
375  	        sss_krb5_free_error_message(krb5_error_ctx, krb5_msg);
376  	    }
377  	    if (ret != EOK) {
378  	        DEBUG(1, ("pam_add_response failed.\n"));
379  	    }
380  	
381  	    ret = pack_response_packet(resp, pam_status, kr->pd);
382  	    if (ret != EOK) {
383  	        DEBUG(1, ("pack_response_packet failed.\n"));
384  	        return NULL;
385  	    }
386  	
387  	    return resp;
388  	}
389  	
390  	static errno_t sendresponse(int fd, krb5_error_code kerr, int pam_status,
391  	                            struct krb5_req *kr)
392  	{
393  	    struct response *resp;
394  	    size_t written;
395  	    int ret;
396  	
397  	    resp = prepare_response_message(kr, kerr, pam_status);
398  	    if (resp == NULL) {
399  	        DEBUG(1, ("prepare_response_message failed.\n"));
400  	        return ENOMEM;
401  	    }
402  	
403  	    written = 0;
404  	    while (written < resp->size) {
405  	        ret = write(fd, resp->buf + written, resp->size - written);
406  	        if (ret == -1) {
407  	            if (errno == EAGAIN || errno == EINTR) {
408  	                continue;
409  	            }
410  	            ret = errno;
411  	            DEBUG(1, ("write failed [%d][%s].\n", ret, strerror(ret)));
412  	            return ret;
413  	        }
414  	        written += ret;
415  	    }
416  	
417  	    return EOK;
418  	}
419  	
420  	static krb5_error_code validate_tgt(struct krb5_req *kr)
421  	{
422  	    krb5_error_code kerr;
423  	    krb5_error_code kt_err;
Event var_decl: Declaring variable "principal" without initializer.
Also see events: [uninit_use]
424  	    char *principal;
425  	    krb5_keytab keytab;
426  	    krb5_kt_cursor cursor;
427  	    krb5_keytab_entry entry;
428  	    krb5_verify_init_creds_opt opt;
429  	
430  	    memset(&keytab, 0, sizeof(keytab));
431  	    kerr = krb5_kt_resolve(kr->ctx, kr->keytab, &keytab);
At conditional (1): "kerr != 0": Taking false branch.
432  	    if (kerr != 0) {
433  	        DEBUG(1, ("error resolving keytab [%s], not verifying TGT.\n",
434  	                  kr->keytab));
435  	        return kerr;
436  	    }
437  	
438  	    memset(&cursor, 0, sizeof(cursor));
439  	    kerr = krb5_kt_start_seq_get(kr->ctx, keytab, &cursor);
At conditional (2): "kerr != 0": Taking false branch.
440  	    if (kerr != 0) {
441  	        DEBUG(1, ("error reading keytab [%s], not verifying TGT.\n",
442  	                  kr->keytab));
443  	        return kerr;
444  	    }
445  	
446  	    /* We look for the first entry from our realm or take the last one */
447  	    memset(&entry, 0, sizeof(entry));
At conditional (3): "(kt_err = krb5_kt_next_entry(kr->ctx, keytab, &entry, &cursor)) == 0": Taking true branch.
448  	    while ((kt_err = krb5_kt_next_entry(kr->ctx, keytab, &entry, &cursor)) == 0) {
At conditional (4): "krb5_realm_compare(kr->ctx, entry.principal, kr->princ)": Taking true branch.
449  	        if (krb5_realm_compare(kr->ctx, entry.principal, kr->princ)) {
At conditional (5): "9 <= debug_level": Taking true branch.
At conditional (6): "debug_timestamps": Taking true branch.
450  	            DEBUG(9, ("Found keytab entry with the realm of the credential.\n"));
451  	            break;
452  	        }
453  	
454  	        kerr = krb5_free_keytab_entry_contents(kr->ctx, &entry);
455  	        if (kerr != 0) {
456  	            DEBUG(1, ("Failed to free keytab entry.\n"));
457  	        }
458  	        memset(&entry, 0, sizeof(entry));
459  	    }
460  	
461  	    /* Close the keytab here.  Even though we're using cursors, the file
462  	     * handle is stored in the krb5_keytab structure, and it gets
463  	     * overwritten when the verify_init_creds() call below creates its own
464  	     * cursor, creating a leak. */
465  	    kerr = krb5_kt_end_seq_get(kr->ctx, keytab, &cursor);
At conditional (7): "kerr != 0": Taking true branch.
466  	    if (kerr != 0) {
At conditional (8): "1 <= debug_level": Taking true branch.
At conditional (9): "debug_timestamps": Taking true branch.
467  	        DEBUG(1, ("krb5_kt_end_seq_get failed, not verifying TGT.\n"));
468  	        goto done;
469  	    }
470  	
471  	    /* check if we got any errors from krb5_kt_next_entry */
472  	    if (kt_err != 0 && kt_err != KRB5_KT_END) {
473  	        DEBUG(1, ("error reading keytab [%s], not verifying TGT.\n",
474  	                  kr->keytab));
475  	        goto done;
476  	    }
477  	
478  	    /* Get the principal to which the key belongs, for logging purposes. */
479  	    principal = NULL;
480  	    kerr = krb5_unparse_name(kr->ctx, entry.principal, &principal);
481  	    if (kerr != 0) {
482  	        DEBUG(1, ("internal error parsing principal name, "
483  	                  "not verifying TGT.\n"));
484  	        goto done;
485  	    }
486  	
487  	
488  	    krb5_verify_init_creds_opt_init(&opt);
489  	    kerr = krb5_verify_init_creds(kr->ctx, kr->creds, entry.principal, keytab,
490  	                                  NULL, &opt);
491  	
492  	    if (kerr == 0) {
493  	        DEBUG(5, ("TGT verified using key for [%s].\n", principal));
494  	    } else {
495  	        DEBUG(1 ,("TGT failed verification using key for [%s].\n", principal));
496  	    }
497  	
498  	done:
At conditional (10): "krb5_kt_close(kr->ctx, keytab) != 0": Taking true branch.
499  	    if (krb5_kt_close(kr->ctx, keytab) != 0) {
At conditional (11): "1 <= debug_level": Taking true branch.
At conditional (12): "debug_timestamps": Taking true branch.
500  	        DEBUG(1, ("krb5_kt_close failed"));
501  	    }
At conditional (13): "krb5_free_keytab_entry_contents(kr->ctx, &entry) != 0": Taking true branch.
502  	    if (krb5_free_keytab_entry_contents(kr->ctx, &entry) != 0) {
At conditional (14): "1 <= debug_level": Taking true branch.
At conditional (15): "debug_timestamps": Taking true branch.
503  	        DEBUG(1, ("Failed to free keytab entry.\n"));
504  	    }
Event uninit_use: Using uninitialized value "principal".
Also see events: [var_decl]
505  	    if (principal != NULL) {
506  	        sss_krb5_free_unparsed_name(kr->ctx, principal);
507  	    }
508  	
509  	    return kerr;
510  	
511  	}
512  	
513  	static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
514  	                                        char *password)
515  	{
516  	    krb5_error_code kerr = 0;
517  	    int ret;
518  	
519  	    kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
520  	                                        password, sss_krb5_prompter, kr, 0,
521  	                                        NULL, kr->options);
522  	    if (kerr != 0) {
523  	        KRB5_DEBUG(1, kerr);
524  	        return kerr;
525  	    }
526  	
527  	    if (kr->validate) {
528  	        kerr = validate_tgt(kr);
529  	        if (kerr != 0) {
530  	            KRB5_DEBUG(1, kerr);
531  	            return kerr;
532  	        }
533  	
534  	        /* We drop root privileges which were needed to read the keytab file
535  	         * for the validation validation of the credentials here to run the
536  	         * ccache I/O operations with user privileges. */
537  	        ret = become_user(kr->uid, kr->gid);
538  	        if (ret != EOK) {
539  	            DEBUG(1, ("become_user failed.\n"));
540  	            return ret;
541  	        }
542  	    } else {
543  	        DEBUG(9, ("TGT validation is disabled.\n"));
544  	    }
545  	
546  	    kerr = create_ccache_file(kr, kr->creds);
547  	    if (kerr != 0) {
548  	        KRB5_DEBUG(1, kerr);
549  	        goto done;
550  	    }
551  	
552  	    kerr = 0;
553  	
554  	done:
555  	    krb5_free_cred_contents(kr->ctx, kr->creds);
556  	
557  	    return kerr;
558  	
559  	}
560  	
561  	static errno_t changepw_child(int fd, struct krb5_req *kr)
562  	{
563  	    int ret;
564  	    krb5_error_code kerr = 0;
565  	    char *pass_str = NULL;
566  	    char *newpass_str = NULL;
567  	    int pam_status = PAM_SYSTEM_ERR;
568  	    int result_code = -1;
569  	    krb5_data result_code_string;
570  	    krb5_data result_string;
571  	    char *user_error_message = NULL;
572  	    size_t user_resp_len;
573  	    uint8_t *user_resp;
574  	    krb5_prompter_fct prompter = sss_krb5_prompter;
575  	
576  	    pass_str = talloc_strndup(kr, (const char *) kr->pd->authtok,
577  	                              kr->pd->authtok_size);
578  	    if (pass_str == NULL) {
579  	        DEBUG(1, ("talloc_strndup failed.\n"));
580  	        kerr = KRB5KRB_ERR_GENERIC;
581  	        goto sendresponse;
582  	    }
583  	
584  	    if (kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) {
585  	        /* We do not need a password expiration warning here. */
586  	        prompter = NULL;
587  	    }
588  	
589  	    kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
590  	                                        pass_str, prompter, kr, 0,
591  	                                        kr->krb5_ctx->changepw_principle,
592  	                                        kr->options);
593  	    if (kerr != 0) {
594  	        KRB5_DEBUG(1, kerr);
595  	        if (kerr == KRB5_KDC_UNREACH) {
596  	            pam_status = PAM_AUTHINFO_UNAVAIL;
597  	        }
598  	        goto sendresponse;
599  	    }
600  	
601  	    memset(pass_str, 0, kr->pd->authtok_size);
602  	    talloc_zfree(pass_str);
603  	    memset(kr->pd->authtok, 0, kr->pd->authtok_size);
604  	
605  	    if (kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) {
606  	        DEBUG(9, ("Initial authentication for change password operation "
607  	                  "successfull.\n"));
608  	        krb5_free_cred_contents(kr->ctx, kr->creds);
609  	        pam_status = PAM_SUCCESS;
610  	        goto sendresponse;
611  	    }
612  	
613  	    newpass_str = talloc_strndup(kr, (const char *) kr->pd->newauthtok,
614  	                              kr->pd->newauthtok_size);
615  	    if (newpass_str == NULL) {
616  	        DEBUG(1, ("talloc_strndup failed.\n"));
617  	        kerr = KRB5KRB_ERR_GENERIC;
618  	        goto sendresponse;
619  	    }
620  	
621  	    memset(&result_code_string, 0, sizeof(krb5_data));
622  	    memset(&result_string, 0, sizeof(krb5_data));
623  	    kerr = krb5_change_password(kr->ctx, kr->creds, newpass_str, &result_code,
624  	                                &result_code_string, &result_string);
625  	
626  	    if (kerr == KRB5_KDC_UNREACH) {
627  	        pam_status = PAM_AUTHTOK_LOCK_BUSY;
628  	        goto sendresponse;
629  	    }
630  	
631  	    if (kerr != 0 || result_code != 0) {
632  	        if (kerr != 0) {
633  	            KRB5_DEBUG(1, kerr);
634  	        } else {
635  	            kerr = KRB5KRB_ERR_GENERIC;
636  	        }
637  	
638  	        if (result_code_string.length > 0) {
639  	            DEBUG(1, ("krb5_change_password failed [%d][%.*s].\n", result_code,
640  	                      result_code_string.length, result_code_string.data));
641  	            user_error_message = talloc_strndup(kr->pd, result_code_string.data,
642  	                                                result_code_string.length);
643  	            if (user_error_message == NULL) {
644  	                DEBUG(1, ("talloc_strndup failed.\n"));
645  	            }
646  	        }
647  	
648  	        if (result_string.length > 0) {
649  	            DEBUG(1, ("krb5_change_password failed [%d][%.*s].\n", result_code,
650  	                      result_string.length, result_string.data));
651  	            talloc_free(user_error_message);
652  	            user_error_message = talloc_strndup(kr->pd, result_string.data,
653  	                                                result_string.length);
654  	            if (user_error_message == NULL) {
655  	                DEBUG(1, ("talloc_strndup failed.\n"));
656  	            }
657  	        }
658  	
659  	        if (user_error_message != NULL) {
660  	            ret = pack_user_info_chpass_error(kr->pd, user_error_message,
661  	                                              &user_resp_len, &user_resp);
662  	            if (ret != EOK) {
663  	                DEBUG(1, ("pack_user_info_chpass_error failed.\n"));
664  	            } else {
665  	                ret = pam_add_response(kr->pd, SSS_PAM_USER_INFO, user_resp_len,
666  	                                       user_resp);
667  	                if (ret != EOK) {
668  	                    DEBUG(1, ("pack_response_packet failed.\n"));
669  	                }
670  	            }
671  	        }
672  	
673  	        pam_status = PAM_AUTHTOK_ERR;
674  	        goto sendresponse;
675  	    }
676  	
677  	    krb5_free_cred_contents(kr->ctx, kr->creds);
678  	
679  	    kerr = get_and_save_tgt(kr, newpass_str);
680  	    memset(newpass_str, 0, kr->pd->newauthtok_size);
681  	    talloc_zfree(newpass_str);
682  	    memset(kr->pd->newauthtok, 0, kr->pd->newauthtok_size);
683  	
684  	    if (kerr != 0) {
685  	        KRB5_DEBUG(1, kerr);
686  	        if (kerr == KRB5_KDC_UNREACH) {
687  	            pam_status = PAM_AUTHINFO_UNAVAIL;
688  	        }
689  	    }
690  	
691  	sendresponse:
692  	    ret = sendresponse(fd, kerr, pam_status, kr);
693  	    if (ret != EOK) {
694  	        DEBUG(1, ("sendresponse failed.\n"));
695  	    }
696  	
697  	    return ret;
698  	}
699  	
700  	static errno_t tgt_req_child(int fd, struct krb5_req *kr)
701  	{
702  	    int ret;
703  	    krb5_error_code kerr = 0;
704  	    char *pass_str = NULL;
705  	    int pam_status = PAM_SYSTEM_ERR;
706  	
707  	    pass_str = talloc_strndup(kr, (const char *) kr->pd->authtok,
708  	                              kr->pd->authtok_size);
709  	    if (pass_str == NULL) {
710  	        DEBUG(1, ("talloc_strndup failed.\n"));
711  	        kerr = KRB5KRB_ERR_GENERIC;
712  	        goto sendresponse;
713  	    }
714  	
715  	    kerr = get_and_save_tgt(kr, pass_str);
716  	
717  	    /* If the password is expired the KDC will always return
718  	       KRB5KDC_ERR_KEY_EXP regardless if the supplied password is correct or
719  	       not. In general the password can still be used to get a changepw ticket.
720  	       So we validate the password by trying to get a changepw ticket. */
721  	    if (kerr == KRB5KDC_ERR_KEY_EXP) {
722  	        kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
723  	                                            pass_str, sss_krb5_prompter, kr, 0,
724  	                                            kr->krb5_ctx->changepw_principle,
725  	                                            kr->options);
726  	        krb5_free_cred_contents(kr->ctx, kr->creds);
727  	        if (kerr == 0) {
728  	            kerr = KRB5KDC_ERR_KEY_EXP;
729  	        }
730  	    }
731  	
732  	    memset(pass_str, 0, kr->pd->authtok_size);
733  	    talloc_zfree(pass_str);
734  	    memset(kr->pd->authtok, 0, kr->pd->authtok_size);
735  	
736  	    if (kerr != 0) {
737  	        KRB5_DEBUG(1, kerr);
738  	        switch (kerr) {
739  	            case KRB5_KDC_UNREACH:
740  	                    pam_status = PAM_AUTHINFO_UNAVAIL;
741  	                    break;
742  	            case KRB5KDC_ERR_KEY_EXP:
743  	                    pam_status = PAM_NEW_AUTHTOK_REQD;
744  	                    break;
745  	            case KRB5KDC_ERR_PREAUTH_FAILED:
746  	                    pam_status = PAM_CRED_ERR;
747  	                    break;
748  	            default:
749  	                    pam_status = PAM_SYSTEM_ERR;
750  	        }
751  	    }
752  	
753  	sendresponse:
754  	    ret = sendresponse(fd, kerr, pam_status, kr);
755  	    if (ret != EOK) {
756  	        DEBUG(1, ("sendresponse failed.\n"));
757  	    }
758  	
759  	    return ret;
760  	}
761  	
762  	static errno_t create_empty_ccache(int fd, struct krb5_req *kr)
763  	{
764  	    int ret;
765  	    int pam_status = PAM_SUCCESS;
766  	
767  	    ret = create_ccache_file(kr, NULL);
768  	    if (ret != 0) {
769  	        KRB5_DEBUG(1, ret);
770  	        pam_status = PAM_SYSTEM_ERR;
771  	    }
772  	
773  	    ret = sendresponse(fd, ret, pam_status, kr);
774  	    if (ret != EOK) {
775  	        DEBUG(1, ("sendresponse failed.\n"));
776  	    }
777  	
778  	    return ret;
779  	}
780  	
781  	static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd,
782  	                             struct krb5_req *kr, uint32_t *offline)
783  	{
784  	    size_t p = 0;
785  	    uint32_t len;
786  	    uint32_t validate;
787  	
788  	    SAFEALIGN_COPY_UINT32_CHECK(&pd->cmd, buf + p, size, &p);
789  	    SAFEALIGN_COPY_UINT32_CHECK(&kr->uid, buf + p, size, &p);
790  	    SAFEALIGN_COPY_UINT32_CHECK(&kr->gid, buf + p, size, &p);
791  	    SAFEALIGN_COPY_UINT32_CHECK(&validate, buf + p, size, &p);
792  	    kr->validate = (validate == 0) ? false : true;
793  	    SAFEALIGN_COPY_UINT32_CHECK(offline, buf + p, size, &p);
794  	
795  	    SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
796  	    if ((p + len ) > size) return EINVAL;
797  	    kr->upn = talloc_strndup(pd, (char *)(buf + p), len);
798  	    if (kr->upn == NULL) return ENOMEM;
799  	    p += len;
800  	
801  	    SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
802  	    if ((p + len ) > size) return EINVAL;
803  	    kr->ccname = talloc_strndup(pd, (char *)(buf + p), len);
804  	    if (kr->ccname == NULL) return ENOMEM;
805  	    p += len;
806  	
807  	    SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
808  	    if ((p + len ) > size) return EINVAL;
809  	    kr->keytab = talloc_strndup(pd, (char *)(buf + p), len);
810  	    if (kr->keytab == NULL) return ENOMEM;
811  	    p += len;
812  	
813  	    SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
814  	    if ((p + len) > size) return EINVAL;
815  	    pd->authtok = (uint8_t *)talloc_strndup(pd, (char *)(buf + p), len);
816  	    if (pd->authtok == NULL) return ENOMEM;
817  	    pd->authtok_size = len + 1;
818  	    p += len;
819  	
820  	    if (pd->cmd == SSS_PAM_CHAUTHTOK) {
821  	        SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
822  	
823  	        if ((p + len) > size) return EINVAL;
824  	        pd->newauthtok = (uint8_t *)talloc_strndup(pd, (char *)(buf + p), len);
825  	        if (pd->newauthtok == NULL) return ENOMEM;
826  	        pd->newauthtok_size = len + 1;
827  	        p += len;
828  	    } else {
829  	        pd->newauthtok = NULL;
830  	        pd->newauthtok_size = 0;
831  	    }
832  	
833  	    return EOK;
834  	}
835  	
836  	static int krb5_cleanup(void *ptr)
837  	{
838  	    struct krb5_req *kr = talloc_get_type(ptr, struct krb5_req);
839  	    if (kr == NULL) return EOK;
840  	
841  	    if (kr->options != NULL) {
842  	        sss_krb5_get_init_creds_opt_free(kr->ctx, kr->options);
843  	    }
844  	
845  	    if (kr->creds != NULL) {
846  	        krb5_free_cred_contents(kr->ctx, kr->creds);
847  	        krb5_free_creds(kr->ctx, kr->creds);
848  	    }
849  	    if (kr->name != NULL)
850  	        sss_krb5_free_unparsed_name(kr->ctx, kr->name);
851  	    if (kr->princ != NULL)
852  	        krb5_free_principal(kr->ctx, kr->princ);
853  	    if (kr->ctx != NULL)
854  	        krb5_free_context(kr->ctx);
855  	
856  	    if (kr->krb5_ctx != NULL) {
857  	        memset(kr->krb5_ctx, 0, sizeof(struct krb5_child_ctx));
858  	    }
859  	    memset(kr, 0, sizeof(struct krb5_req));
860  	
861  	    return EOK;
862  	}
863  	
864  	static int krb5_setup(struct krb5_req *kr, uint32_t offline)
865  	{
866  	    krb5_error_code kerr = 0;
867  	
868  	    kr->krb5_ctx = talloc_zero(kr, struct krb5_child_ctx);
869  	    if (kr->krb5_ctx == NULL) {
870  	        DEBUG(1, ("talloc failed.\n"));
871  	        kerr = ENOMEM;
872  	        goto failed;
873  	    }
874  	
875  	    kr->krb5_ctx->changepw_principle = getenv(SSSD_KRB5_CHANGEPW_PRINCIPLE);
876  	    if (kr->krb5_ctx->changepw_principle == NULL) {
877  	        DEBUG(1, ("Cannot read [%s] from environment.\n",
878  	                  SSSD_KRB5_CHANGEPW_PRINCIPLE));
879  	        if (kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
880  	            goto failed;
881  	        }
882  	    }
883  	
884  	    kr->krb5_ctx->realm = getenv(SSSD_KRB5_REALM);
885  	    if (kr->krb5_ctx->realm == NULL) {
886  	        DEBUG(2, ("Cannot read [%s] from environment.\n", SSSD_KRB5_REALM));
887  	    }
888  	
889  	    switch(kr->pd->cmd) {
890  	        case SSS_PAM_AUTHENTICATE:
891  	            /* If we are offline, we need to create an empty ccache file */
892  	            if (offline) {
893  	                kr->child_req = create_empty_ccache;
894  	            } else {
895  	                kr->child_req = tgt_req_child;
896  	            }
897  	            break;
898  	        case SSS_PAM_CHAUTHTOK:
899  	        case SSS_PAM_CHAUTHTOK_PRELIM:
900  	            kr->child_req = changepw_child;
901  	            break;
902  	        default:
903  	            DEBUG(1, ("PAM command [%d] not supported.\n", kr->pd->cmd));
904  	            kerr = EINVAL;
905  	            goto failed;
906  	    }
907  	
908  	    kerr = krb5_init_context(&kr->ctx);
909  	    if (kerr != 0) {
910  	        KRB5_DEBUG(1, kerr);
911  	        goto failed;
912  	    }
913  	
914  	    kerr = krb5_parse_name(kr->ctx, kr->upn, &kr->princ);
915  	    if (kerr != 0) {
916  	        KRB5_DEBUG(1, kerr);
917  	        goto failed;
918  	    }
919  	
920  	    kerr = krb5_unparse_name(kr->ctx, kr->princ, &kr->name);
921  	    if (kerr != 0) {
922  	        KRB5_DEBUG(1, kerr);
923  	        goto failed;
924  	    }
925  	
926  	    kr->creds = calloc(1, sizeof(krb5_creds));
927  	    if (kr->creds == NULL) {
928  	        DEBUG(1, ("talloc_zero failed.\n"));
929  	        kerr = ENOMEM;
930  	        goto failed;
931  	    }
932  	
933  	    kerr = sss_krb5_get_init_creds_opt_alloc(kr->ctx, &kr->options);
934  	    if (kerr != 0) {
935  	        KRB5_DEBUG(1, kerr);
936  	        goto failed;
937  	    }
938  	
939  	    /* A prompter is used to catch messages about when a password will
940  	     * expired. The library shall not use the prompter to ask for a new password
941  	     * but shall return KRB5KDC_ERR_KEY_EXP. */
942  	    krb5_get_init_creds_opt_set_change_password_prompt(kr->options, 0);
943  	    if (kerr != 0) {
944  	        KRB5_DEBUG(1, kerr);
945  	        goto failed;
946  	    }
947  	
948  	/* TODO: set options, e.g.
949  	 *  krb5_get_init_creds_opt_set_tkt_life
950  	 *  krb5_get_init_creds_opt_set_renew_life
951  	 *  krb5_get_init_creds_opt_set_forwardable
952  	 *  krb5_get_init_creds_opt_set_proxiable
953  	 *  krb5_get_init_creds_opt_set_etype_list
954  	 *  krb5_get_init_creds_opt_set_address_list
955  	 *  krb5_get_init_creds_opt_set_preauth_list
956  	 *  krb5_get_init_creds_opt_set_salt
957  	 *  krb5_get_init_creds_opt_set_change_password_prompt
958  	 *  krb5_get_init_creds_opt_set_pa
959  	 */
960  	
961  	    return EOK;
962  	
963  	failed:
964  	
965  	    return kerr;
966  	}
967  	
968  	int main(int argc, const char *argv[])
969  	{
970  	    uint8_t *buf = NULL;
971  	    int ret;
972  	    ssize_t len = 0;
973  	    struct pam_data *pd = NULL;
974  	    struct krb5_req *kr = NULL;
975  	    uint32_t offline;
976  	    int opt;
977  	    poptContext pc;
978  	    int debug_fd = -1;
979  	
980  	    struct poptOption long_options[] = {
981  	        POPT_AUTOHELP
982  	        {"debug-level", 'd', POPT_ARG_INT, &debug_level, 0,
983  	         _("Debug level"), NULL},
984  	        {"debug-timestamps", 0, POPT_ARG_INT, &debug_timestamps, 0,
985  	         _("Add debug timestamps"), NULL},
986  	        {"debug-fd", 0, POPT_ARG_INT, &debug_fd, 0,
987  	         _("An open file descriptor for the debug logs"), NULL},
988  	        POPT_TABLEEND
989  	    };
990  	
991  	
992  	    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
993  	    while((opt = poptGetNextOpt(pc)) != -1) {
994  	        switch(opt) {
995  	        default:
996  	        fprintf(stderr, "\nInvalid option %s: %s\n\n",
997  	                  poptBadOption(pc, 0), poptStrerror(opt));
998  	            poptPrintUsage(pc, stderr, 0);
999  	            _exit(-1);
1000 	        }
1001 	    }
1002 	
1003 	    poptFreeContext(pc);
1004 	
1005 	    DEBUG(7, ("krb5_child started.\n"));
1006 	
1007 	    pd = talloc(NULL, struct pam_data);
1008 	    if (pd == NULL) {
1009 	        DEBUG(1, ("malloc failed.\n"));
1010 	        _exit(-1);
1011 	    }
1012 	
1013 	    debug_prg_name = talloc_asprintf(pd, "[sssd[krb5_child[%d]]]", getpid());
1014 	
1015 	    if (debug_fd != -1) {
1016 	        ret = set_debug_file_from_fd(debug_fd);
1017 	        if (ret != EOK) {
1018 	            DEBUG(1, ("set_debug_file_from_fd failed.\n"));
1019 	        }
1020 	    }
1021 	
1022 	    buf = talloc_size(pd, sizeof(uint8_t)*IN_BUF_SIZE);
1023 	    if (buf == NULL) {
1024 	        DEBUG(1, ("malloc failed.\n"));
1025 	        _exit(-1);
1026 	    }
1027 	
1028 	    while ((ret = read(STDIN_FILENO, buf + len, IN_BUF_SIZE - len)) != 0) {
1029 	        if (ret == -1) {
1030 	            if (errno == EINTR || errno == EAGAIN) {
1031 	                continue;
1032 	            }
1033 	            DEBUG(1, ("read failed [%d][%s].\n", errno, strerror(errno)));
1034 	            goto fail;
1035 	        } else if (ret > 0) {
1036 	            len += ret;
1037 	            if (len > IN_BUF_SIZE) {
1038 	                DEBUG(1, ("read too much, this should never happen.\n"));
1039 	                goto fail;
1040 	            }
1041 	            continue;
1042 	        } else {
1043 	            DEBUG(1, ("unexpected return code of read [%d].\n", ret));
1044 	            goto fail;
1045 	        }
1046 	    }
1047 	    close(STDIN_FILENO);
1048 	
1049 	    kr = talloc_zero(pd, struct krb5_req);
1050 	    if (kr == NULL) {
1051 	        DEBUG(1, ("talloc failed.\n"));
1052 	        goto fail;
1053 	    }
1054 	    talloc_set_destructor((TALLOC_CTX *) kr, krb5_cleanup);
1055 	    kr->pd = pd;
1056 	
1057 	    ret = unpack_buffer(buf, len, pd, kr, &offline);
1058 	    if (ret != EOK) {
1059 	        DEBUG(1, ("unpack_buffer failed.\n"));
1060 	        goto fail;
1061 	    }
1062 	
1063 	    ret = krb5_setup(kr, offline);
1064 	    if (ret != EOK) {
1065 	        DEBUG(1, ("krb5_setup failed.\n"));
1066 	        goto fail;
1067 	    }
1068 	
1069 	    ret = kr->child_req(STDOUT_FILENO, kr);
1070 	    if (ret != EOK) {
1071 	        DEBUG(1, ("Child request failed.\n"));
1072 	        goto fail;
1073 	    }
1074 	
1075 	    close(STDOUT_FILENO);
1076 	    talloc_free(pd);
1077 	
1078 	    return 0;
1079 	
1080 	fail:
1081 	    close(STDOUT_FILENO);
1082 	    talloc_free(pd);
1083 	    exit(-1);
1084 	}