1/*
2 * Copyright (c) 2005, PADL Software Pty Ltd.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * 3. Neither the name of PADL Software nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include "kcm_locl.h"
34
35krb5_error_code
36kcm_ccache_refresh(krb5_context context,
37		   kcm_ccache ccache,
38		   time_t *expire)
39{
40    krb5_error_code ret;
41    krb5_creds in, *out;
42    krb5_kdc_flags flags;
43    krb5_const_realm realm;
44    krb5_ccache_data ccdata;
45    struct kcm_creds *kcred;
46
47    memset(&in, 0, sizeof(in));
48    *expire = 0;
49
50    KCM_ASSERT_VALID(ccache);
51
52    kcm_log(0, "Refresh credentials");
53
54    if (ccache->client == NULL) {
55	/* no primary principal */
56	kcm_log(0, "Renew credentials requested but no client principal");
57	return KRB5_CC_NOTFOUND;
58    }
59
60    /* Fake up an internal ccache */
61    kcm_internal_ccache(context, ccache, &ccdata);
62
63    /* Find principal */
64    in.client = ccache->client;
65
66    if (ccache->server != NULL) {
67	ret = krb5_copy_principal(context, ccache->server, &in.server);
68	if (ret) {
69	    kcm_log(0, "Failed to copy service principal: %s", ccache->name);
70	    goto out;
71	}
72    } else {
73	realm = krb5_principal_get_realm(context, in.client);
74	ret = krb5_make_principal(context, &in.server, realm,
75				  KRB5_TGS_NAME, realm, NULL);
76	if (ret) {
77	    kcm_log(0, "Failed to make TGS principal for realm %s", realm);
78	    goto out;
79	}
80    }
81
82    if (ccache->tkt_life)
83	in.times.endtime = time(NULL) + ccache->tkt_life;
84    if (ccache->renew_life)
85	in.times.renew_till = time(NULL) + ccache->renew_life;
86
87    flags.i = 0;
88    flags.b.renewable = TRUE;
89    flags.b.renew = TRUE;
90
91    /*
92     * Capture the forwardable/proxyable bit from previous matching
93     * service ticket, we can't use initial since that get lost in the
94     * first renewal.
95     */
96
97    for (kcred = ccache->creds; kcred != NULL; kcred = kcred->next) {
98	if (krb5_principal_compare(context, kcred->cred.server, in.server)) {
99	    flags.b.forwardable = kcred->cred.flags.b.forwardable;
100	    flags.b.proxiable = kcred->cred.flags.b.proxiable;
101	    break;
102	}
103    }
104
105    ret = krb5_get_kdc_cred(context,
106			    &ccdata,
107			    flags,
108			    NULL,
109			    NULL,
110			    &in,
111			    &out);
112    if (ret) {
113	kcm_log(0, "Failed to renew credentials for cache %s",
114		ccache->name);
115	goto out;
116    }
117
118    *expire = out->times.endtime;
119
120    /* Swap them in */
121    kcm_ccache_remove_creds_internal(context, ccache);
122
123    ret = kcm_ccache_store_cred_internal(context, ccache, out, NULL, 0);
124    if (ret) {
125	kcm_log(0, "Failed to store credentials for cache %s",
126		ccache->name);
127	krb5_free_creds(context, out);
128	goto out;
129    }
130
131    free(out); /* but not contents */
132out:
133    krb5_free_principal(context, in.server);
134
135    return ret;
136}
137
138