Let the carnage begin....
[metze/old/v3-2-winbind-ndr.git] / source / 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                         return talloc_free(entry) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
78                 }
79         }
80         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
81 }
82
83 static void krb5_ticket_refresh_handler(struct timed_event *te,
84                                         const struct timeval *now,
85                                         void *private_data)
86 {
87         struct WINBINDD_CCACHE_ENTRY *entry =
88                 talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY);
89         int ret;
90         time_t new_start;
91         struct timeval t;
92
93
94         DEBUG(10,("krb5_ticket_refresh_handler called\n"));
95         DEBUGADD(10,("event called for: %s, %s\n", entry->ccname, entry->username));
96
97         talloc_free(entry->event);
98
99 #ifdef HAVE_KRB5
100
101         /* Kinit again if we have the user password and we can't renew the old
102          * tgt anymore */
103
104         if ((entry->renew_until < time(NULL)) && (entry->pass != NULL)) {
105              
106                 seteuid(entry->uid);
107
108                 ret = kerberos_kinit_password(entry->principal_name,
109                                               entry->pass,
110                                               0, /* hm, can we do time correction here ? */
111                                               &entry->refresh_time,
112                                               &entry->renew_until,
113                                               entry->ccname,
114                                               False, /* no PAC required anymore */
115                                               WINBINDD_PAM_AUTH_KRB5_RENEW_TIME);
116                 seteuid(0);
117
118                 if (ret) {
119                         DEBUG(3,("could not re-kinit: %s\n", error_message(ret)));
120                         talloc_free(entry->event);
121                         return;
122                 }
123
124                 DEBUG(10,("successful re-kinit for: %s in ccache: %s\n", 
125                         entry->principal_name, entry->ccname));
126
127                 new_start = entry->refresh_time;
128
129                 goto done;
130         }
131
132         seteuid(entry->uid);
133
134         ret = smb_krb5_renew_ticket(entry->ccname, 
135                                     entry->principal_name,
136                                     entry->service,
137                                     &new_start);
138         seteuid(0);
139
140         if (ret) {
141                 DEBUG(3,("could not renew tickets: %s\n", error_message(ret)));
142                 /* maybe we are beyond the renewing window */
143                 return;
144         }
145
146 done:
147
148         t = timeval_set(new_start, 0);
149
150         entry->event = add_timed_event(mem_ctx, 
151                                        t,
152                                        "krb5_ticket_refresh_handler",
153                                        krb5_ticket_refresh_handler,
154                                        entry);
155
156 #endif
157 }
158
159 NTSTATUS add_ccache_to_list(const char *princ_name,
160                             const char *ccname,
161                             const char *service,
162                             const char *username, 
163                             const char *sid_string,
164                             const char *pass,
165                             uid_t uid,
166                             time_t create_time, 
167                             time_t ticket_end, 
168                             time_t renew_until, 
169                             BOOL schedule_refresh_event)
170 {
171         struct WINBINDD_CCACHE_ENTRY *new_entry = NULL;
172         NTSTATUS status;
173
174         if ((username == NULL && sid_string == NULL && princ_name == NULL) || 
175             ccname == NULL) {
176                 return NT_STATUS_INVALID_PARAMETER;
177         }
178
179         status = init_ccache_list();
180         if (!NT_STATUS_IS_OK(status)) {
181                 return status;
182         }
183
184         if (mem_ctx == NULL) {
185                 return NT_STATUS_NO_MEMORY;
186         }
187
188         if (ccache_entry_count() + 1 > MAX_CCACHES) {
189                 DEBUG(10,("add_ccache_to_list: max number of ccaches reached\n"));
190                 return NT_STATUS_NO_MORE_ENTRIES;
191         }
192
193         new_entry = TALLOC_P(mem_ctx, struct WINBINDD_CCACHE_ENTRY);
194         if (new_entry == NULL) {
195                 return NT_STATUS_NO_MEMORY;
196         }
197
198         ZERO_STRUCTP(new_entry);
199
200         if (username) {
201                 new_entry->username = talloc_strdup(mem_ctx, username);
202                 NT_STATUS_HAVE_NO_MEMORY(new_entry->username);
203         }
204         if (sid_string) {
205                 new_entry->sid_string = talloc_strdup(mem_ctx, sid_string);
206                 NT_STATUS_HAVE_NO_MEMORY(new_entry->sid_string);
207         }
208         if (princ_name) {
209                 new_entry->principal_name = talloc_strdup(mem_ctx, princ_name);
210                 NT_STATUS_HAVE_NO_MEMORY(new_entry->principal_name);
211         }
212         if (service) {
213                 new_entry->service = talloc_strdup(mem_ctx, service);
214                 NT_STATUS_HAVE_NO_MEMORY(new_entry->service);
215         }
216         if (pass) {
217                 new_entry->pass = talloc_strdup(mem_ctx, pass);
218                 NT_STATUS_HAVE_NO_MEMORY(new_entry->pass);
219         }
220
221         new_entry->create_time = create_time;
222         new_entry->renew_until = renew_until;
223         new_entry->ccname = talloc_strdup(mem_ctx, ccname);
224         if (new_entry->ccname == NULL) {
225                 return NT_STATUS_NO_MEMORY;
226         }
227         new_entry->uid = uid;
228
229
230 #ifndef WITH_KCM /* no point in doing the refresh in KCM and by ourself */
231
232         if (schedule_refresh_event && renew_until > 0) {
233
234                 struct timeval t = timeval_set((ticket_end -1 ), 0);
235
236                 new_entry->event = add_timed_event(mem_ctx, 
237                                                    t,
238                                                    "krb5_ticket_refresh_handler",
239                                                    krb5_ticket_refresh_handler,
240                                                    new_entry);
241         }
242 #endif /* WITH_KCM */
243
244         DLIST_ADD(ccache_list, new_entry);
245
246         DEBUG(10,("add_ccache_to_list: added ccache [%s] for user [%s] to the list\n", ccname, username));
247
248         return NT_STATUS_OK;
249 }
250
251 NTSTATUS destroy_ccache_list(void)
252 {
253         return talloc_destroy(mem_ctx) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
254 }
255
256 NTSTATUS init_ccache_list(void)
257 {
258         if (ccache_list) {
259                 return NT_STATUS_OK;
260         }
261
262         mem_ctx = talloc_init("winbindd_ccache_krb5_handling");
263         if (mem_ctx == NULL) {
264                 return NT_STATUS_NO_MEMORY;
265         }
266
267         ZERO_STRUCTP(ccache_list);
268
269         return NT_STATUS_OK;
270 }