r13571: Replace all calls to talloc_free() with thye TALLOC_FREE()
[kai/samba.git] / source3 / nsswitch / winbindd_cred_cache.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon - krb5 credential cache funcions
5
6    Copyright (C) Guenther Deschner 2005
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "winbindd.h"
25 #undef DBGC_CLASS
26 #define DBGC_CLASS DBGC_WINBIND
27
28 #define MAX_CCACHES 100 
29
30 static struct WINBINDD_CCACHE_ENTRY *ccache_list;
31
32 static TALLOC_CTX *mem_ctx;
33
34 const char *get_ccache_name_by_username(const char *username) 
35 {
36         struct WINBINDD_CCACHE_ENTRY *entry;
37
38         for (entry = ccache_list; entry; entry = entry->next) {
39                 if (strequal(entry->username, username)) {
40                         return entry->ccname;
41                 }
42         }
43         return NULL;
44 }
45
46 struct WINBINDD_CCACHE_ENTRY *get_ccache_by_username(const char *username)
47 {
48         struct WINBINDD_CCACHE_ENTRY *entry;
49
50         for (entry = ccache_list; entry; entry = entry->next) {
51                 if (strequal(entry->username, username)) {
52                         return entry;
53                 }
54         }
55         return NULL;
56 }
57
58 static int ccache_entry_count(void)
59 {
60         struct WINBINDD_CCACHE_ENTRY *entry;
61         int i = 0;
62
63         for (entry = ccache_list; entry; entry = entry->next) {
64                 i++;
65         }
66         return i;
67 }
68
69 NTSTATUS remove_ccache_by_ccname(const char *ccname)
70 {
71         struct WINBINDD_CCACHE_ENTRY *entry;
72
73         for (entry = ccache_list; entry; entry = entry->next) {
74                 if (strequal(entry->ccname, ccname)) {
75                         DLIST_REMOVE(ccache_list, entry);
76                         TALLOC_FREE(entry->event); /* unregisters events */
77                         TALLOC_FREE(entry);
78                         return NT_STATUS_OK;
79                 }
80         }
81         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
82 }
83
84 static void krb5_ticket_refresh_handler(struct timed_event *te,
85                                         const struct timeval *now,
86                                         void *private_data)
87 {
88         struct WINBINDD_CCACHE_ENTRY *entry =
89                 talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY);
90         int ret;
91         time_t new_start;
92         struct timeval t;
93
94
95         DEBUG(10,("krb5_ticket_refresh_handler called\n"));
96         DEBUGADD(10,("event called for: %s, %s\n", entry->ccname, entry->username));
97
98         TALLOC_FREE(entry->event);
99
100 #ifdef HAVE_KRB5
101
102         /* Kinit again if we have the user password and we can't renew the old
103          * tgt anymore */
104
105         if ((entry->renew_until < time(NULL)) && (entry->pass != NULL)) {
106              
107                 seteuid(entry->uid);
108
109                 ret = kerberos_kinit_password(entry->principal_name,
110                                               entry->pass,
111                                               0, /* hm, can we do time correction here ? */
112                                               &entry->refresh_time,
113                                               &entry->renew_until,
114                                               entry->ccname,
115                                               False, /* no PAC required anymore */
116                                               WINBINDD_PAM_AUTH_KRB5_RENEW_TIME);
117                 seteuid(0);
118
119                 if (ret) {
120                         DEBUG(3,("could not re-kinit: %s\n", error_message(ret)));
121                         TALLOC_FREE(entry->event);
122                         return;
123                 }
124
125                 DEBUG(10,("successful re-kinit for: %s in ccache: %s\n", 
126                         entry->principal_name, entry->ccname));
127
128                 new_start = entry->refresh_time;
129
130                 goto done;
131         }
132
133         seteuid(entry->uid);
134
135         ret = smb_krb5_renew_ticket(entry->ccname, 
136                                     entry->principal_name,
137                                     entry->service,
138                                     &new_start);
139         seteuid(0);
140
141         if (ret) {
142                 DEBUG(3,("could not renew tickets: %s\n", error_message(ret)));
143                 /* maybe we are beyond the renewing window */
144                 return;
145         }
146
147 done:
148
149         t = timeval_set(new_start, 0);
150
151         entry->event = add_timed_event(mem_ctx, 
152                                        t,
153                                        "krb5_ticket_refresh_handler",
154                                        krb5_ticket_refresh_handler,
155                                        entry);
156
157 #endif
158 }
159
160 NTSTATUS add_ccache_to_list(const char *princ_name,
161                             const char *ccname,
162                             const char *service,
163                             const char *username, 
164                             const char *sid_string,
165                             const char *pass,
166                             uid_t uid,
167                             time_t create_time, 
168                             time_t ticket_end, 
169                             time_t renew_until, 
170                             BOOL schedule_refresh_event)
171 {
172         struct WINBINDD_CCACHE_ENTRY *new_entry = NULL;
173         NTSTATUS status;
174
175         if ((username == NULL && sid_string == NULL && princ_name == NULL) || 
176             ccname == NULL) {
177                 return NT_STATUS_INVALID_PARAMETER;
178         }
179
180         status = init_ccache_list();
181         if (!NT_STATUS_IS_OK(status)) {
182                 return status;
183         }
184
185         if (mem_ctx == NULL) {
186                 return NT_STATUS_NO_MEMORY;
187         }
188
189         if (ccache_entry_count() + 1 > MAX_CCACHES) {
190                 DEBUG(10,("add_ccache_to_list: max number of ccaches reached\n"));
191                 return NT_STATUS_NO_MORE_ENTRIES;
192         }
193
194         new_entry = TALLOC_P(mem_ctx, struct WINBINDD_CCACHE_ENTRY);
195         if (new_entry == NULL) {
196                 return NT_STATUS_NO_MEMORY;
197         }
198
199         ZERO_STRUCTP(new_entry);
200
201         if (username) {
202                 new_entry->username = talloc_strdup(mem_ctx, username);
203                 NT_STATUS_HAVE_NO_MEMORY(new_entry->username);
204         }
205         if (sid_string) {
206                 new_entry->sid_string = talloc_strdup(mem_ctx, sid_string);
207                 NT_STATUS_HAVE_NO_MEMORY(new_entry->sid_string);
208         }
209         if (princ_name) {
210                 new_entry->principal_name = talloc_strdup(mem_ctx, princ_name);
211                 NT_STATUS_HAVE_NO_MEMORY(new_entry->principal_name);
212         }
213         if (service) {
214                 new_entry->service = talloc_strdup(mem_ctx, service);
215                 NT_STATUS_HAVE_NO_MEMORY(new_entry->service);
216         }
217         if (pass) {
218                 new_entry->pass = talloc_strdup(mem_ctx, pass);
219                 NT_STATUS_HAVE_NO_MEMORY(new_entry->pass);
220         }
221
222         new_entry->create_time = create_time;
223         new_entry->renew_until = renew_until;
224         new_entry->ccname = talloc_strdup(mem_ctx, ccname);
225         if (new_entry->ccname == NULL) {
226                 return NT_STATUS_NO_MEMORY;
227         }
228         new_entry->uid = uid;
229
230
231 #ifndef WITH_KCM /* no point in doing the refresh in KCM and by ourself */
232
233         if (schedule_refresh_event && renew_until > 0) {
234
235                 struct timeval t = timeval_set((ticket_end -1 ), 0);
236
237                 new_entry->event = add_timed_event(mem_ctx, 
238                                                    t,
239                                                    "krb5_ticket_refresh_handler",
240                                                    krb5_ticket_refresh_handler,
241                                                    new_entry);
242         }
243 #endif /* WITH_KCM */
244
245         DLIST_ADD(ccache_list, new_entry);
246
247         DEBUG(10,("add_ccache_to_list: added ccache [%s] for user [%s] to the list\n", ccname, username));
248
249         return NT_STATUS_OK;
250 }
251
252 NTSTATUS destroy_ccache_list(void)
253 {
254         return talloc_destroy(mem_ctx) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
255 }
256
257 NTSTATUS init_ccache_list(void)
258 {
259         if (ccache_list) {
260                 return NT_STATUS_OK;
261         }
262
263         mem_ctx = talloc_init("winbindd_ccache_krb5_handling");
264         if (mem_ctx == NULL) {
265                 return NT_STATUS_NO_MEMORY;
266         }
267
268         ZERO_STRUCTP(ccache_list);
269
270         return NT_STATUS_OK;
271 }