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/globals.h" 31 32/******************************************************************** 33********************************************************************/ 34 35static struct db_context *session_db_ctx(void) 36{ 37 if (session_db_ctx_ptr) 38 return session_db_ctx_ptr; 39 40 session_db_ctx_ptr = db_open(NULL, lock_path("sessionid.tdb"), 0, 41 TDB_CLEAR_IF_FIRST|TDB_DEFAULT, 42 O_RDWR | O_CREAT, 0644); 43 return session_db_ctx_ptr; 44} 45 46bool session_init(void) 47{ 48 if (session_db_ctx() == NULL) { 49 DEBUG(1,("session_init: failed to open sessionid tdb\n")); 50 return False; 51 } 52 53 return True; 54} 55 56/******************************************************************** 57 called when a session is created 58********************************************************************/ 59 60bool session_claim(user_struct *vuser) 61{ 62 TDB_DATA key, data; 63 int i = 0; 64 struct sessionid sessionid; 65 struct server_id pid = procid_self(); 66 fstring keystr; 67 const char * hostname; 68 struct db_context *ctx; 69 struct db_record *rec; 70 NTSTATUS status; 71 char addr[INET6_ADDRSTRLEN]; 72 73 vuser->session_keystr = NULL; 74 75 /* don't register sessions for the guest user - its just too 76 expensive to go through pam session code for browsing etc */ 77 if (vuser->server_info->guest) { 78 return True; 79 } 80 81 if (!(ctx = session_db_ctx())) { 82 return False; 83 } 84 85 ZERO_STRUCT(sessionid); 86 87 data.dptr = NULL; 88 data.dsize = 0; 89 90 if (lp_utmp()) { 91 92 for (i=1;i<MAX_SESSION_ID;i++) { 93 94 /* 95 * This is very inefficient and needs fixing -- vl 96 */ 97 98 struct server_id sess_pid; 99 100 snprintf(keystr, sizeof(keystr), "ID/%d", i); 101 key = string_term_tdb_data(keystr); 102 103 rec = ctx->fetch_locked(ctx, NULL, key); 104 105 if (rec == NULL) { 106 DEBUG(1, ("Could not lock \"%s\"\n", keystr)); 107 return False; 108 } 109 110 if (rec->value.dsize != sizeof(sessionid)) { 111 DEBUG(1, ("Re-using invalid record\n")); 112 break; 113 } 114 115 memcpy(&sess_pid, 116 ((char *)rec->value.dptr) 117 + offsetof(struct sessionid, pid), 118 sizeof(sess_pid)); 119 120 if (!process_exists(sess_pid)) { 121 DEBUG(5, ("%s has died -- re-using session\n", 122 procid_str_static(&sess_pid))); 123 break; 124 } 125 126 TALLOC_FREE(rec); 127 } 128 129 if (i == MAX_SESSION_ID) { 130 SMB_ASSERT(rec == NULL); 131 DEBUG(1,("session_claim: out of session IDs " 132 "(max is %d)\n", MAX_SESSION_ID)); 133 return False; 134 } 135 136 snprintf(sessionid.id_str, sizeof(sessionid.id_str), 137 SESSION_UTMP_TEMPLATE, i); 138 } else 139 { 140 snprintf(keystr, sizeof(keystr), "ID/%s/%u", 141 procid_str_static(&pid), vuser->vuid); 142 key = string_term_tdb_data(keystr); 143 144 rec = ctx->fetch_locked(ctx, NULL, key); 145 146 if (rec == NULL) { 147 DEBUG(1, ("Could not lock \"%s\"\n", keystr)); 148 return False; 149 } 150 151 snprintf(sessionid.id_str, sizeof(sessionid.id_str), 152 SESSION_TEMPLATE, (long unsigned int)sys_getpid(), 153 vuser->vuid); 154 } 155 156 SMB_ASSERT(rec != NULL); 157 158 /* If 'hostname lookup' == yes, then do the DNS lookup. This is 159 needed because utmp and PAM both expect DNS names 160 161 client_name() handles this case internally. 162 */ 163 164 hostname = client_name(get_client_fd()); 165 if (strcmp(hostname, "UNKNOWN") == 0) { 166 hostname = client_addr(get_client_fd(),addr,sizeof(addr)); 167 } 168 169 fstrcpy(sessionid.username, vuser->server_info->unix_name); 170 fstrcpy(sessionid.hostname, hostname); 171 sessionid.id_num = i; /* Only valid for utmp sessions */ 172 sessionid.pid = pid; 173 sessionid.uid = vuser->server_info->utok.uid; 174 sessionid.gid = vuser->server_info->utok.gid; 175 fstrcpy(sessionid.remote_machine, get_remote_machine_name()); 176 fstrcpy(sessionid.ip_addr_str, 177 client_addr(get_client_fd(),addr,sizeof(addr))); 178 sessionid.connect_start = time(NULL); 179 180 if (!smb_pam_claim_session(sessionid.username, sessionid.id_str, 181 sessionid.hostname)) { 182 DEBUG(1,("pam_session rejected the session for %s [%s]\n", 183 sessionid.username, sessionid.id_str)); 184 185 TALLOC_FREE(rec); 186 return False; 187 } 188 189 data.dptr = (uint8 *)&sessionid; 190 data.dsize = sizeof(sessionid); 191 192 status = rec->store(rec, data, TDB_REPLACE); 193 194 TALLOC_FREE(rec); 195 196 if (!NT_STATUS_IS_OK(status)) { 197 DEBUG(1,("session_claim: unable to create session id " 198 "record: %s\n", nt_errstr(status))); 199 return False; 200 } 201 202 if (lp_utmp()) { 203 sys_utmp_claim(sessionid.username, sessionid.hostname, 204 sessionid.ip_addr_str, 205 sessionid.id_str, sessionid.id_num); 206 } 207 208 vuser->session_keystr = talloc_strdup(vuser, keystr); 209 if (!vuser->session_keystr) { 210 DEBUG(0, ("session_claim: talloc_strdup() failed for session_keystr\n")); 211 return False; 212 } 213 return True; 214} 215 216/******************************************************************** 217 called when a session is destroyed 218********************************************************************/ 219 220void session_yield(user_struct *vuser) 221{ 222 TDB_DATA key; 223 struct sessionid sessionid; 224 struct db_context *ctx; 225 struct db_record *rec; 226 227 if (!(ctx = session_db_ctx())) return; 228 229 if (!vuser->session_keystr) { 230 return; 231 } 232 233 key = string_term_tdb_data(vuser->session_keystr); 234 235 if (!(rec = ctx->fetch_locked(ctx, NULL, key))) { 236 return; 237 } 238 239 if (rec->value.dsize != sizeof(sessionid)) 240 return; 241 242 memcpy(&sessionid, rec->value.dptr, sizeof(sessionid)); 243 244 if (lp_utmp()) { 245 sys_utmp_yield(sessionid.username, sessionid.hostname, 246 sessionid.ip_addr_str, 247 sessionid.id_str, sessionid.id_num); 248 } 249 250 smb_pam_close_session(sessionid.username, sessionid.id_str, 251 sessionid.hostname); 252 253 rec->delete_rec(rec); 254 255 TALLOC_FREE(rec); 256} 257 258/******************************************************************** 259********************************************************************/ 260 261static bool session_traverse(int (*fn)(struct db_record *db, 262 void *private_data), 263 void *private_data) 264{ 265 struct db_context *ctx; 266 267 if (!(ctx = session_db_ctx())) { 268 DEBUG(3, ("No tdb opened\n")); 269 return False; 270 } 271 272 ctx->traverse_read(ctx, fn, private_data); 273 return True; 274} 275 276/******************************************************************** 277********************************************************************/ 278 279struct session_list { 280 TALLOC_CTX *mem_ctx; 281 int count; 282 struct sessionid *sessions; 283}; 284 285static int gather_sessioninfo(struct db_record *rec, void *state) 286{ 287 struct session_list *sesslist = (struct session_list *) state; 288 const struct sessionid *current = 289 (const struct sessionid *) rec->value.dptr; 290 291 sesslist->sessions = TALLOC_REALLOC_ARRAY( 292 sesslist->mem_ctx, sesslist->sessions, struct sessionid, 293 sesslist->count+1); 294 295 if (!sesslist->sessions) { 296 sesslist->count = 0; 297 return -1; 298 } 299 300 memcpy(&sesslist->sessions[sesslist->count], current, 301 sizeof(struct sessionid)); 302 303 sesslist->count++; 304 305 DEBUG(7,("gather_sessioninfo session from %s@%s\n", 306 current->username, current->remote_machine)); 307 308 return 0; 309} 310 311/******************************************************************** 312********************************************************************/ 313 314int list_sessions(TALLOC_CTX *mem_ctx, struct sessionid **session_list) 315{ 316 struct session_list sesslist; 317 318 sesslist.mem_ctx = mem_ctx; 319 sesslist.count = 0; 320 sesslist.sessions = NULL; 321 322 if (!session_traverse(gather_sessioninfo, (void *) &sesslist)) { 323 DEBUG(3, ("Session traverse failed\n")); 324 SAFE_FREE(sesslist.sessions); 325 *session_list = NULL; 326 return 0; 327 } 328 329 *session_list = sesslist.sessions; 330 return sesslist.count; 331} 332