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