Merge commit 'release-4-0-0alpha15' into master4-tmp
[nivanova/samba-autobuild/.git] / source3 / smbd / session.c
1 /* 
2    Unix SMB/CIFS implementation.
3    session handling for utmp and PAM
4
5    Copyright (C) tridge@samba.org       2001
6    Copyright (C) abartlet@samba.org     2001
7    Copyright (C) Gerald (Jerry) Carter  2006   
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 /* a "session" is claimed when we do a SessionSetupX operation
24    and is yielded when the corresponding vuid is destroyed.
25
26    sessions are used to populate utmp and PAM session structures
27 */
28
29 #include "includes.h"
30 #include "smbd/smbd.h"
31 #include "smbd/globals.h"
32 #include "dbwrap.h"
33 #include "session.h"
34 #include "auth.h"
35
36 /********************************************************************
37  called when a session is created
38 ********************************************************************/
39
40 bool session_claim(struct smbd_server_connection *sconn, user_struct *vuser)
41 {
42         struct server_id pid = sconn_server_id(sconn);
43         TDB_DATA data;
44         int i = 0;
45         struct sessionid sessionid;
46         fstring keystr;
47         struct db_record *rec;
48         NTSTATUS status;
49
50         vuser->session_keystr = NULL;
51
52         /* don't register sessions for the guest user - its just too
53            expensive to go through pam session code for browsing etc */
54         if (vuser->session_info->guest) {
55                 return True;
56         }
57
58         if (!sessionid_init()) {
59                 return False;
60         }
61
62         ZERO_STRUCT(sessionid);
63
64         data.dptr = NULL;
65         data.dsize = 0;
66
67         if (lp_utmp()) {
68
69                 for (i=1;i<MAX_SESSION_ID;i++) {
70
71                         /*
72                          * This is very inefficient and needs fixing -- vl
73                          */
74
75                         struct server_id sess_pid;
76
77                         snprintf(keystr, sizeof(keystr), "ID/%d", i);
78
79                         rec = sessionid_fetch_record(NULL, keystr);
80                         if (rec == NULL) {
81                                 DEBUG(1, ("Could not lock \"%s\"\n", keystr));
82                                 return False;
83                         }
84
85                         if (rec->value.dsize != sizeof(sessionid)) {
86                                 DEBUG(1, ("Re-using invalid record\n"));
87                                 break;
88                         }
89
90                         memcpy(&sess_pid,
91                                ((char *)rec->value.dptr)
92                                + offsetof(struct sessionid, pid),
93                                sizeof(sess_pid));
94
95                         if (!process_exists(sess_pid)) {
96                                 DEBUG(5, ("%s has died -- re-using session\n",
97                                           procid_str_static(&sess_pid)));
98                                 break;
99                         }
100
101                         TALLOC_FREE(rec);
102                 }
103
104                 if (i == MAX_SESSION_ID) {
105                         SMB_ASSERT(rec == NULL);
106                         DEBUG(1,("session_claim: out of session IDs "
107                                  "(max is %d)\n", MAX_SESSION_ID));
108                         return False;
109                 }
110
111                 snprintf(sessionid.id_str, sizeof(sessionid.id_str),
112                          SESSION_UTMP_TEMPLATE, i);
113         } else
114         {
115                 snprintf(keystr, sizeof(keystr), "ID/%s/%u",
116                          procid_str_static(&pid), vuser->vuid);
117
118                 rec = sessionid_fetch_record(NULL, keystr);
119                 if (rec == NULL) {
120                         DEBUG(1, ("Could not lock \"%s\"\n", keystr));
121                         return False;
122                 }
123
124                 snprintf(sessionid.id_str, sizeof(sessionid.id_str),
125                          SESSION_TEMPLATE, (long unsigned int)sys_getpid(),
126                          vuser->vuid);
127         }
128
129         SMB_ASSERT(rec != NULL);
130
131         /* If 'hostname lookup' == yes, then do the DNS lookup.  This is
132            needed because utmp and PAM both expect DNS names
133
134            client_name() handles this case internally.
135         */
136
137         fstrcpy(sessionid.username, vuser->session_info->unix_name);
138         fstrcpy(sessionid.hostname, sconn->client_id.name);
139         sessionid.id_num = i;  /* Only valid for utmp sessions */
140         sessionid.pid = pid;
141         sessionid.uid = vuser->session_info->utok.uid;
142         sessionid.gid = vuser->session_info->utok.gid;
143         fstrcpy(sessionid.remote_machine, get_remote_machine_name());
144         fstrcpy(sessionid.ip_addr_str, sconn->client_id.addr);
145         sessionid.connect_start = time(NULL);
146
147         if (!smb_pam_claim_session(sessionid.username, sessionid.id_str,
148                                    sessionid.hostname)) {
149                 DEBUG(1,("pam_session rejected the session for %s [%s]\n",
150                                 sessionid.username, sessionid.id_str));
151
152                 TALLOC_FREE(rec);
153                 return False;
154         }
155
156         data.dptr = (uint8 *)&sessionid;
157         data.dsize = sizeof(sessionid);
158
159         status = rec->store(rec, data, TDB_REPLACE);
160
161         TALLOC_FREE(rec);
162
163         if (!NT_STATUS_IS_OK(status)) {
164                 DEBUG(1,("session_claim: unable to create session id "
165                          "record: %s\n", nt_errstr(status)));
166                 return False;
167         }
168
169         if (lp_utmp()) {
170                 sys_utmp_claim(sessionid.username, sessionid.hostname,
171                                sessionid.ip_addr_str,
172                                sessionid.id_str, sessionid.id_num);
173         }
174
175         vuser->session_keystr = talloc_strdup(vuser, keystr);
176         if (!vuser->session_keystr) {
177                 DEBUG(0, ("session_claim:  talloc_strdup() failed for session_keystr\n"));
178                 return False;
179         }
180         return True;
181 }
182
183 /********************************************************************
184  called when a session is destroyed
185 ********************************************************************/
186
187 void session_yield(user_struct *vuser)
188 {
189         struct sessionid sessionid;
190         struct db_record *rec;
191
192         if (!vuser->session_keystr) {
193                 return;
194         }
195
196         rec = sessionid_fetch_record(NULL, vuser->session_keystr);
197         if (rec == NULL) {
198                 return;
199         }
200
201         if (rec->value.dsize != sizeof(sessionid))
202                 return;
203
204         memcpy(&sessionid, rec->value.dptr, sizeof(sessionid));
205
206         if (lp_utmp()) {
207                 sys_utmp_yield(sessionid.username, sessionid.hostname, 
208                                sessionid.ip_addr_str,
209                                sessionid.id_str, sessionid.id_num);
210         }
211
212         smb_pam_close_session(sessionid.username, sessionid.id_str,
213                               sessionid.hostname);
214
215         rec->delete_rec(rec);
216
217         TALLOC_FREE(rec);
218 }
219
220 /********************************************************************
221 ********************************************************************/
222
223 struct session_list {
224         TALLOC_CTX *mem_ctx;
225         int count;
226         struct sessionid *sessions;
227 };
228
229 static int gather_sessioninfo(const char *key, struct sessionid *session,
230                               void *private_data)
231 {
232         struct session_list *sesslist = (struct session_list *)private_data;
233
234         sesslist->sessions = talloc_realloc(
235                 sesslist->mem_ctx, sesslist->sessions, struct sessionid,
236                 sesslist->count+1);
237
238         if (!sesslist->sessions) {
239                 sesslist->count = 0;
240                 return -1;
241         }
242
243         memcpy(&sesslist->sessions[sesslist->count], session,
244                sizeof(struct sessionid));
245
246         sesslist->count++;
247
248         DEBUG(7, ("gather_sessioninfo session from %s@%s\n",
249                   session->username, session->remote_machine));
250
251         return 0;
252 }
253
254 /********************************************************************
255 ********************************************************************/
256
257 int list_sessions(TALLOC_CTX *mem_ctx, struct sessionid **session_list)
258 {
259         struct session_list sesslist;
260         int ret;
261
262         sesslist.mem_ctx = mem_ctx;
263         sesslist.count = 0;
264         sesslist.sessions = NULL;
265
266         ret = sessionid_traverse_read(gather_sessioninfo, (void *) &sesslist);
267         if (ret < 0) {
268                 DEBUG(3, ("Session traverse failed\n"));
269                 SAFE_FREE(sesslist.sessions);
270                 *session_list = NULL;
271                 return 0;
272         }
273
274         *session_list = sesslist.sessions;
275         return sesslist.count;
276 }