Patch from Simo:
[Samba.git] / source / rpc_server / srv_netlog_nt.c
blob065f94702c89ddc6fee495928906f2845ecd5bb2
1 /*
2 * Unix SMB/Netbios implementation.
3 * Version 1.9.
4 * RPC Pipe client / server routines
5 * Copyright (C) Andrew Tridgell 1992-1997,
6 * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
7 * Copyright (C) Paul Ashton 1997.
8 * Copyright (C) Jeremy Allison 1998-2001.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 /* This is the implementation of the netlogon pipe. */
27 #include "includes.h"
29 extern int DEBUGLEVEL;
31 extern BOOL sam_logon_in_ssb;
32 extern pstring samlogon_user;
33 extern pstring global_myname;
34 extern DOM_SID global_sam_sid;
36 /*************************************************************************
37 init_net_r_req_chal:
38 *************************************************************************/
40 static void init_net_r_req_chal(NET_R_REQ_CHAL *r_c,
41 DOM_CHAL *srv_chal, int status)
43 DEBUG(6,("init_net_r_req_chal: %d\n", __LINE__));
44 memcpy(r_c->srv_chal.data, srv_chal->data, sizeof(srv_chal->data));
45 r_c->status = status;
48 /*************************************************************************
49 error messages cropping up when using nltest.exe...
50 *************************************************************************/
52 #define ERROR_NO_SUCH_DOMAIN 0x54b
53 #define ERROR_NO_LOGON_SERVERS 0x51f
55 /*************************************************************************
56 net_reply_logon_ctrl2:
57 *************************************************************************/
59 uint32 _net_logon_ctrl2(pipes_struct *p, NET_Q_LOGON_CTRL2 *q_u, NET_R_LOGON_CTRL2 *r_u)
61 /* lkclXXXX - guess what - absolutely no idea what these are! */
62 uint32 flags = 0x0;
63 uint32 pdc_connection_status = 0x0;
64 uint32 logon_attempts = 0x0;
65 uint32 tc_status = ERROR_NO_LOGON_SERVERS;
66 char *trusted_domain = "test_domain";
68 DEBUG(6,("_net_logon_ctrl2: %d\n", __LINE__));
70 /* set up the Logon Control2 response */
71 init_r_logon_ctrl2(r_u, q_u->query_level,
72 flags, pdc_connection_status, logon_attempts,
73 tc_status, trusted_domain);
75 DEBUG(6,("_net_logon_ctrl2: %d\n", __LINE__));
77 return r_u->status;
80 /*************************************************************************
81 net_reply_trust_dom_list:
82 *************************************************************************/
84 uint32 _net_trust_dom_list(pipes_struct *p, NET_Q_TRUST_DOM_LIST *q_u, NET_R_TRUST_DOM_LIST *r_u)
86 char *trusted_domain = "test_domain";
87 uint32 num_trust_domains = 1;
89 DEBUG(6,("_net_trust_dom_list: %d\n", __LINE__));
91 /* set up the Trusted Domain List response */
92 init_r_trust_dom(r_u, num_trust_domains, trusted_domain);
94 DEBUG(6,("_net_trust_dom_list: %d\n", __LINE__));
96 return r_u->status;
99 /***********************************************************************************
100 init_net_r_srv_pwset:
101 ***********************************************************************************/
103 static void init_net_r_srv_pwset(NET_R_SRV_PWSET *r_s,
104 DOM_CRED *srv_cred, int status)
106 DEBUG(5,("init_net_r_srv_pwset: %d\n", __LINE__));
108 memcpy(&r_s->srv_cred, srv_cred, sizeof(r_s->srv_cred));
109 r_s->status = status;
111 DEBUG(5,("init_net_r_srv_pwset: %d\n", __LINE__));
114 /******************************************************************
115 gets a machine password entry. checks access rights of the host.
116 ******************************************************************/
118 static BOOL get_md4pw(char *md4pw, char *mach_acct)
120 SAM_ACCOUNT *sampass = NULL;
121 uint8 *pass;
122 BOOL ret;
124 #if 0
126 * Currently this code is redundent as we already have a filter
127 * by hostname list. What this code really needs to do is to
128 * get a hosts allowed/hosts denied list from the SAM database
129 * on a per user basis, and make the access decision there.
130 * I will leave this code here for now as a reminder to implement
131 * this at a later date. JRA.
134 if (!allow_access(lp_domain_hostsdeny(), lp_domain_hostsallow(),
135 client_name(), client_addr()))
137 DEBUG(0,("get_md4pw: Workstation %s denied access to domain\n", mach_acct));
138 return False;
140 #endif /* 0 */
142 if(!pdb_init_sam(&sampass))
143 return False;
145 /* JRA. This is ok as it is only used for generating the challenge. */
146 become_root();
147 ret=pdb_getsampwnam(sampass, mach_acct);
148 unbecome_root();
150 if (ret==False) {
151 DEBUG(0,("get_md4pw: Workstation %s: no account in domain\n", mach_acct));
152 pdb_free_sam(sampass);
153 return False;
156 if (!(pdb_get_acct_ctrl(sampass) & ACB_DISABLED) && ((pass=pdb_get_nt_passwd(sampass)) != NULL)) {
157 memcpy(md4pw, pass, 16);
158 dump_data(5, md4pw, 16);
159 pdb_free_sam(sampass);
160 return True;
163 DEBUG(0,("get_md4pw: Workstation %s: no account in domain\n", mach_acct));
164 pdb_free_sam(sampass);
165 return False;
169 /*************************************************************************
170 _net_req_chal
171 *************************************************************************/
173 uint32 _net_req_chal(pipes_struct *p, NET_Q_REQ_CHAL *q_u, NET_R_REQ_CHAL *r_u)
175 uint32 status = NT_STATUS_NOPROBLEMO;
176 fstring mach_acct;
178 if (!get_valid_user_struct(p->vuid))
179 return NT_STATUS_NO_SUCH_USER;
181 fstrcpy(mach_acct, dos_unistrn2(q_u->uni_logon_clnt.buffer,
182 q_u->uni_logon_clnt.uni_str_len));
184 strlower(mach_acct);
185 fstrcat(mach_acct, "$");
187 if (get_md4pw((char *)p->dc.md4pw, mach_acct)) {
188 /* copy the client credentials */
189 memcpy(p->dc.clnt_chal.data , q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
190 memcpy(p->dc.clnt_cred.challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
192 /* create a server challenge for the client */
193 /* Set these to random values. */
194 generate_random_buffer(p->dc.srv_chal.data, 8, False);
196 memcpy(p->dc.srv_cred.challenge.data, p->dc.srv_chal.data, 8);
198 memset((char *)p->dc.sess_key, '\0', sizeof(p->dc.sess_key));
200 /* from client / server challenges and md4 password, generate sess key */
201 cred_session_key(&p->dc.clnt_chal, &p->dc.srv_chal,
202 (char *)p->dc.md4pw, p->dc.sess_key);
204 /* Save the machine account name. */
205 fstrcpy(p->dc.mach_acct, mach_acct);
207 } else {
208 /* lkclXXXX take a guess at a good error message to return :-) */
209 status = NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
212 /* set up the LSA REQUEST CHALLENGE response */
213 init_net_r_req_chal(r_u, &p->dc.srv_chal, status);
215 return r_u->status;
218 /*************************************************************************
219 init_net_r_auth:
220 *************************************************************************/
222 static void init_net_r_auth(NET_R_AUTH *r_a, DOM_CHAL *resp_cred, int status)
224 memcpy(r_a->srv_chal.data, resp_cred->data, sizeof(resp_cred->data));
225 r_a->status = status;
228 /*************************************************************************
229 _net_auth
230 *************************************************************************/
232 uint32 _net_auth(pipes_struct *p, NET_Q_AUTH *q_u, NET_R_AUTH *r_u)
234 uint32 status = NT_STATUS_NOPROBLEMO;
235 DOM_CHAL srv_cred;
236 UTIME srv_time;
238 if (!get_valid_user_struct(p->vuid))
239 return NT_STATUS_NO_SUCH_USER;
241 srv_time.time = 0;
243 /* check that the client credentials are valid */
244 if (cred_assert(&q_u->clnt_chal, p->dc.sess_key, &p->dc.clnt_cred.challenge, srv_time)) {
246 /* create server challenge for inclusion in the reply */
247 cred_create(p->dc.sess_key, &p->dc.srv_cred.challenge, srv_time, &srv_cred);
249 /* copy the received client credentials for use next time */
250 memcpy(p->dc.clnt_cred.challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
251 memcpy(p->dc.srv_cred .challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
252 } else {
253 status = NT_STATUS_ACCESS_DENIED;
256 /* set up the LSA AUTH 2 response */
257 init_net_r_auth(r_u, &srv_cred, status);
259 return r_u->status;
262 /*************************************************************************
263 init_net_r_auth_2:
264 *************************************************************************/
266 static void init_net_r_auth_2(NET_R_AUTH_2 *r_a,
267 DOM_CHAL *resp_cred, NEG_FLAGS *flgs, int status)
269 memcpy(r_a->srv_chal.data, resp_cred->data, sizeof(resp_cred->data));
270 memcpy(&r_a->srv_flgs, flgs, sizeof(r_a->srv_flgs));
271 r_a->status = status;
274 /*************************************************************************
275 _net_auth_2
276 *************************************************************************/
278 uint32 _net_auth_2(pipes_struct *p, NET_Q_AUTH_2 *q_u, NET_R_AUTH_2 *r_u)
280 uint32 status = NT_STATUS_NOPROBLEMO;
281 DOM_CHAL srv_cred;
282 UTIME srv_time;
283 NEG_FLAGS srv_flgs;
285 if (!get_valid_user_struct(p->vuid))
286 return NT_STATUS_NO_SUCH_USER;
288 srv_time.time = 0;
290 /* check that the client credentials are valid */
291 if (cred_assert(&q_u->clnt_chal, p->dc.sess_key, &p->dc.clnt_cred.challenge, srv_time)) {
293 /* create server challenge for inclusion in the reply */
294 cred_create(p->dc.sess_key, &p->dc.srv_cred.challenge, srv_time, &srv_cred);
296 /* copy the received client credentials for use next time */
297 memcpy(p->dc.clnt_cred.challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
298 memcpy(p->dc.srv_cred .challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
299 } else {
300 status = NT_STATUS_ACCESS_DENIED;
303 srv_flgs.neg_flags = 0x000001ff;
305 /* set up the LSA AUTH 2 response */
306 init_net_r_auth_2(r_u, &srv_cred, &srv_flgs, status);
308 return r_u->status;
311 /*************************************************************************
312 _net_srv_pwset
313 *************************************************************************/
315 uint32 _net_srv_pwset(pipes_struct *p, NET_Q_SRV_PWSET *q_u, NET_R_SRV_PWSET *r_u)
317 uint32 status = NT_STATUS_WRONG_PASSWORD;
318 DOM_CRED srv_cred;
319 pstring mach_acct;
320 SAM_ACCOUNT *sampass=NULL;
321 BOOL ret = False;
322 unsigned char pwd[16];
323 int i;
325 if (!get_valid_user_struct(p->vuid))
326 return NT_STATUS_NO_SUCH_USER;
328 /* checks and updates credentials. creates reply credentials */
329 if (!deal_with_creds(p->dc.sess_key, &p->dc.clnt_cred, &q_u->clnt_id.cred, &srv_cred))
330 return NT_STATUS_INVALID_HANDLE;
332 memcpy(&p->dc.srv_cred, &p->dc.clnt_cred, sizeof(p->dc.clnt_cred));
334 DEBUG(5,("_net_srv_pwset: %d\n", __LINE__));
336 pstrcpy(mach_acct, dos_unistrn2(q_u->clnt_id.login.uni_acct_name.buffer,
337 q_u->clnt_id.login.uni_acct_name.uni_str_len));
339 DEBUG(3,("Server Password Set Wksta:[%s]\n", mach_acct));
341 pdb_init_sam(&sampass);
343 become_root();
344 ret=pdb_getsampwnam(sampass, mach_acct);
345 unbecome_root();
347 /* Ensure the account exists and is a machine account. */
349 if (ret==False || !(pdb_get_acct_ctrl(sampass) & ACB_WSTRUST)) {
350 pdb_free_sam(sampass);
351 return NT_STATUS_NO_SUCH_USER;
355 * Check the machine account name we're changing is the same
356 * as the one we've authenticated from. This prevents arbitrary
357 * machines changing other machine account passwords.
360 if (!strequal(mach_acct, p->dc.mach_acct)) {
361 pdb_free_sam(sampass);
362 return NT_STATUS_ACCESS_DENIED;
366 DEBUG(100,("Server password set : new given value was :\n"));
367 for(i = 0; i < 16; i++)
368 DEBUG(100,("%02X ", q_u->pwd[i]));
369 DEBUG(100,("\n"));
371 cred_hash3( pwd, q_u->pwd, p->dc.sess_key, 0);
373 /* lies! nt and lm passwords are _not_ the same: don't care */
374 pdb_set_lanman_passwd (sampass, pwd);
375 pdb_set_nt_passwd (sampass, pwd);
376 pdb_set_acct_ctrl (sampass, ACB_WSTRUST);
378 become_root();
379 ret = pdb_update_sam_account (sampass,False);
380 unbecome_root();
382 if (ret)
383 status = NT_STATUS_NOPROBLEMO;
385 /* set up the LSA Server Password Set response */
386 init_net_r_srv_pwset(r_u, &srv_cred, status);
388 pdb_free_sam(sampass);
389 return r_u->status;
393 /*************************************************************************
394 _net_sam_logoff:
395 *************************************************************************/
397 uint32 _net_sam_logoff(pipes_struct *p, NET_Q_SAM_LOGOFF *q_u, NET_R_SAM_LOGOFF *r_u)
399 DOM_CRED srv_cred;
401 if (!get_valid_user_struct(p->vuid))
402 return NT_STATUS_NO_SUCH_USER;
404 /* checks and updates credentials. creates reply credentials */
405 if (!deal_with_creds(p->dc.sess_key, &p->dc.clnt_cred,
406 &q_u->sam_id.client.cred, &srv_cred))
407 return NT_STATUS_INVALID_HANDLE;
409 memcpy(&p->dc.srv_cred, &p->dc.clnt_cred, sizeof(p->dc.clnt_cred));
411 /* XXXX maybe we want to say 'no', reject the client's credentials */
412 r_u->buffer_creds = 1; /* yes, we have valid server credentials */
413 memcpy(&r_u->srv_creds, &srv_cred, sizeof(r_u->srv_creds));
415 r_u->status = NT_STATUS_NOPROBLEMO;
417 return r_u->status;
420 /*************************************************************************
421 net_login_interactive:
422 *************************************************************************/
424 static uint32 net_login_interactive(NET_ID_INFO_1 *id1, SAM_ACCOUNT *sampass, pipes_struct *p)
426 uint32 status = 0x0;
428 char nt_pwd[16];
429 char lm_pwd[16];
430 unsigned char key[16];
432 memset(key, 0, 16);
433 memcpy(key, p->dc.sess_key, 8);
435 memcpy(lm_pwd, id1->lm_owf.data, 16);
436 memcpy(nt_pwd, id1->nt_owf.data, 16);
438 #ifdef DEBUG_PASSWORD
439 DEBUG(100,("key:"));
440 dump_data(100, (char *)key, 16);
442 DEBUG(100,("lm owf password:"));
443 dump_data(100, lm_pwd, 16);
445 DEBUG(100,("nt owf password:"));
446 dump_data(100, nt_pwd, 16);
447 #endif
449 SamOEMhash((uchar *)lm_pwd, key, False);
450 SamOEMhash((uchar *)nt_pwd, key, False);
452 #ifdef DEBUG_PASSWORD
453 DEBUG(100,("decrypt of lm owf password:"));
454 dump_data(100, lm_pwd, 16);
456 DEBUG(100,("decrypt of nt owf password:"));
457 dump_data(100, nt_pwd, 16);
458 #endif
460 if (memcmp(pdb_get_lanman_passwd(sampass), lm_pwd, 16) != 0 ||
461 memcmp(pdb_get_nt_passwd(sampass), nt_pwd, 16) != 0) {
462 status = NT_STATUS_WRONG_PASSWORD;
465 return status;
468 /*************************************************************************
469 _net_login_network:
470 *************************************************************************/
472 static uint32 net_login_network(NET_ID_INFO_2 *id2, SAM_ACCOUNT *sampass)
474 uint8 *nt_pwd, *lanman_pwd;
476 DEBUG(5,("net_login_network: lm_len: %d nt_len: %d\n",
477 id2->hdr_lm_chal_resp.str_str_len,
478 id2->hdr_nt_chal_resp.str_str_len));
480 /* JRA. Check the NT password first if it exists - this is a higher quality
481 password, if it exists and it doesn't match - fail. */
483 nt_pwd = pdb_get_nt_passwd(sampass);
484 lanman_pwd = pdb_get_lanman_passwd(sampass);
486 if (id2->hdr_nt_chal_resp.str_str_len == 24 && nt_pwd) {
487 if(smb_password_check((char *)id2->nt_chal_resp.buffer,
488 nt_pwd, id2->lm_chal))
489 return NT_STATUS_NOPROBLEMO;
490 else
491 return NT_STATUS_WRONG_PASSWORD;
494 /* lkclXXXX this is not a good place to put disabling of LM hashes in.
495 if that is to be done, first move this entire function into a
496 library routine that calls the two smb_password_check() functions.
497 if disabling LM hashes (which nt can do for security reasons) then
498 an attempt should be made to disable them everywhere (which nt does
499 not do, for various security-hole reasons).
502 if (id2->hdr_lm_chal_resp.str_str_len == 24 && lanman_pwd &&
503 smb_password_check((char *)id2->lm_chal_resp.buffer,
504 lanman_pwd, id2->lm_chal))
505 return NT_STATUS_NOPROBLEMO;
507 /* oops! neither password check succeeded */
509 return NT_STATUS_WRONG_PASSWORD;
512 /*************************************************************************
513 _net_sam_logon
514 *************************************************************************/
516 uint32 _net_sam_logon(pipes_struct *p, NET_Q_SAM_LOGON *q_u, NET_R_SAM_LOGON *r_u)
518 uint32 status = NT_STATUS_NOPROBLEMO;
519 NET_USER_INFO_3 *usr_info = NULL;
520 DOM_CRED srv_cred;
521 SAM_ACCOUNT *sampass = NULL;
522 uint16 acct_ctrl;
523 UNISTR2 *uni_samlogon_user = NULL;
524 fstring nt_username;
525 BOOL ret;
527 usr_info = (NET_USER_INFO_3 *)talloc(p->mem_ctx, sizeof(NET_USER_INFO_3));
528 if (!usr_info)
529 return NT_STATUS_NO_MEMORY;
531 ZERO_STRUCTP(usr_info);
533 if (!get_valid_user_struct(p->vuid))
534 return NT_STATUS_NO_SUCH_USER;
536 /* checks and updates credentials. creates reply credentials */
537 if (!deal_with_creds(p->dc.sess_key, &p->dc.clnt_cred, &q_u->sam_id.client.cred, &srv_cred))
538 return NT_STATUS_INVALID_HANDLE;
539 else
540 memcpy(&p->dc.srv_cred, &p->dc.clnt_cred, sizeof(p->dc.clnt_cred));
542 r_u->buffer_creds = 1; /* yes, we have valid server credentials */
543 memcpy(&r_u->srv_creds, &srv_cred, sizeof(r_u->srv_creds));
545 /* store the user information, if there is any. */
546 r_u->user = usr_info;
547 r_u->switch_value = 0; /* indicates no info */
548 r_u->auth_resp = 1; /* authoritative response */
549 r_u->switch_value = 3; /* indicates type of validation user info */
551 /* find the username */
553 switch (q_u->sam_id.logon_level) {
554 case INTERACTIVE_LOGON_TYPE:
555 uni_samlogon_user = &q_u->sam_id.ctr->auth.id1.uni_user_name;
557 DEBUG(3,("SAM Logon (Interactive). Domain:[%s]. ", lp_workgroup()));
558 break;
559 case NET_LOGON_TYPE:
560 uni_samlogon_user = &q_u->sam_id.ctr->auth.id2.uni_user_name;
562 DEBUG(3,("SAM Logon (Network). Domain:[%s]. ", lp_workgroup()));
563 break;
564 default:
565 DEBUG(2,("SAM Logon: unsupported switch value\n"));
566 return NT_STATUS_INVALID_INFO_CLASS;
567 } /* end switch */
569 /* check username exists */
571 pstrcpy(nt_username, dos_unistrn2(uni_samlogon_user->buffer, uni_samlogon_user->uni_str_len));
573 DEBUG(3,("User:[%s]\n", nt_username));
576 * Convert to a UNIX username.
579 map_username(nt_username);
581 pdb_init_sam(&sampass);
583 /* get the account information */
584 become_root();
585 ret = pdb_getsampwnam(sampass, nt_username);
586 unbecome_root();
588 if (ret == False){
589 pdb_free_sam(sampass);
590 return NT_STATUS_NO_SUCH_USER;
593 acct_ctrl = pdb_get_acct_ctrl(sampass);
595 if (acct_ctrl & ACB_DISABLED) {
596 pdb_free_sam(sampass);
597 return NT_STATUS_ACCOUNT_DISABLED;
600 /* Validate password - if required. */
602 if (!(acct_ctrl & ACB_PWNOTREQ)) {
603 switch (q_u->sam_id.logon_level) {
604 case INTERACTIVE_LOGON_TYPE:
605 /* interactive login. */
606 status = net_login_interactive(&q_u->sam_id.ctr->auth.id1, sampass, p);
607 break;
608 case NET_LOGON_TYPE:
609 /* network login. lm challenge and 24 byte responses */
610 status = net_login_network(&q_u->sam_id.ctr->auth.id2, sampass);
611 break;
615 if (status != NT_STATUS_NOPROBLEMO) {
616 pdb_free_sam(sampass);
617 return status;
620 /* lkclXXXX this is the point at which, if the login was
621 successful, that the SAM Local Security Authority should
622 record that the user is logged in to the domain.
626 DOM_GID *gids = NULL;
627 int num_gids = 0;
628 pstring my_name;
629 pstring my_workgroup;
630 pstring domain_groups;
632 /* set up pointer indicating user/password failed to be found */
633 usr_info->ptr_user_info = 0;
635 /* XXXX hack to get standard_sub_basic() to use sam logon username */
636 /* possibly a better way would be to do a become_user() call */
637 sam_logon_in_ssb = True;
638 pstrcpy(samlogon_user, nt_username);
640 pstrcpy(my_workgroup, lp_workgroup());
641 pstrcpy(my_name, global_myname);
642 strupper(my_name);
645 * This is the point at which we get the group
646 * database - we should be getting the gid_t list
647 * from /etc/group and then turning the uids into
648 * rids and then into machine sids for this user.
649 * JRA.
652 get_domain_user_groups(domain_groups, nt_username);
655 * make_dom_gids allocates the gids array. JRA.
657 gids = NULL;
658 num_gids = make_dom_gids(p->mem_ctx, domain_groups, &gids);
660 sam_logon_in_ssb = False;
662 init_net_user_info3(p->mem_ctx, usr_info, sampass,
663 0, /* logon_count */
664 0, /* bad_pw_count */
665 num_gids, /* uint32 num_groups */
666 gids , /* DOM_GID *gids */
667 0x20 , /* uint32 user_flgs (?) */
668 NULL, /* char sess_key[16] */
669 my_name , /* char *logon_srv */
670 my_workgroup, /* char *logon_dom */
671 &global_sam_sid, /* DOM_SID *dom_sid */
672 NULL); /* char *other_sids */
674 pdb_free_sam(sampass);
675 return status;