38
39
#include "common/dict.h"
39
40
#include "common/expr.h"
42
#define HOST_NAME_MAX 255
43
#endif /* not HOST_NAME_MAX */
45
/* set up a connection and try to bind with the specified DN and password
46
returns a NSLCD_PAM_* error code */
42
/* set up a connection and try to bind with the specified DN and password,
43
returns an LDAP result code */
47
44
static int try_bind(const char *userdn,const char *password)
49
46
MYLDAP_SESSION *session;
47
MYLDAP_SEARCH *search;
49
static const char *attrs[2];
52
51
/* set up a new connection */
53
52
session=myldap_create_session();
55
return NSLCD_PAM_AUTH_ERR;
54
return LDAP_UNAVAILABLE;
56
55
/* set up credentials for the session */
57
56
myldap_set_credentials(session,userdn,password);
58
57
/* perform search for own object (just to do any kind of search) */
59
username=lookup_dn2uid(session,userdn,&rc);
60
search=myldap_search(session,userdn,LDAP_SCOPE_BASE,"(objectClass=*)",attrs,&rc);
61
if ((search==NULL)||(rc!=LDAP_SUCCESS))
65
log_log(LOG_WARNING,"lookup of %s failed: %s",userdn,ldap_err2string(rc));
69
entry=myldap_get_entry(search,&rc);
70
if ((entry==NULL)||(rc!=LDAP_SUCCESS))
73
rc=LDAP_NO_RESULTS_RETURNED;
74
log_log(LOG_WARNING,"lookup of %s failed: %s",userdn,ldap_err2string(rc));
62
77
/* close the session */
63
78
myldap_session_close(session);
64
/* handle the results */
67
case LDAP_SUCCESS: return NSLCD_PAM_SUCCESS;
68
case LDAP_INVALID_CREDENTIALS: return NSLCD_PAM_AUTH_ERR;
69
default: return NSLCD_PAM_AUTH_ERR;
73
/* ensure that both userdn and username are filled in from the entry */
74
static int validate_user(MYLDAP_SESSION *session,char *userdn,size_t userdnsz,
75
char *username,size_t usernamesz)
83
/* ensure that both userdn and username are filled in from the entry,
84
returns an LDAP result code */
85
static MYLDAP_ENTRY *validate_user(MYLDAP_SESSION *session,
86
char *username,int *rcp)
77
89
MYLDAP_ENTRY *entry=NULL;
80
90
/* check username for validity */
81
91
if (!isvalidname(username))
83
log_log(LOG_WARNING,"\"%s\": invalid user name",username);
86
/* look up user DN if not known */
89
/* get the user entry based on the username */
90
entry=uid2entry(session,username);
93
log_log(LOG_WARNING,"\"%s\": user not found",username);
97
myldap_cpy_dn(entry,userdn,userdnsz);
98
if (strcasecmp(userdn,"unknown")==0)
100
log_log(LOG_WARNING,"\"%s\": user has no DN",username);
103
/* get the "real" username */
104
value=myldap_get_rdn_value(entry,attmap_passwd_uid);
107
/* get the username from the uid attribute */
108
values=myldap_get_values(entry,attmap_passwd_uid);
109
if ((values==NULL)||(values[0]==NULL))
110
log_log(LOG_WARNING,"\"%s\": DN %s is missing a %s attribute",
111
username,userdn,attmap_passwd_uid);
114
/* check the username */
115
if ((value==NULL)||!isvalidname(value)||strlen(value)>=usernamesz)
117
log_log(LOG_WARNING,"\"%s\": DN %s has invalid username",username,userdn);
120
/* check if the username is different and update it if needed */
121
if (strcmp(username,value)!=0)
123
log_log(LOG_INFO,"username changed from \"%s\" to \"%s\"",username,value);
124
strcpy(username,value);
127
/* all check passed */
93
log_log(LOG_WARNING,"\"%s\": name denied by validnames option",username);
94
*rcp=LDAP_NO_SUCH_OBJECT;
97
/* get the user entry based on the username */
98
entry=uid2entry(session,username,&rc);
101
if (rc==LDAP_SUCCESS)
102
rc=LDAP_NO_SUCH_OBJECT;
103
log_log(LOG_WARNING,"\"%s\": user not found: %s",username,ldap_err2string(rc));
109
/* update the username value from the entry if needed */
110
static void update_username(MYLDAP_ENTRY *entry,char *username,size_t username_len)
114
/* get the "real" username */
115
value=myldap_get_rdn_value(entry,attmap_passwd_uid);
118
/* get the username from the uid attribute */
119
values=myldap_get_values(entry,attmap_passwd_uid);
120
if ((values==NULL)||(values[0]==NULL))
121
log_log(LOG_WARNING,"\"%s\": DN %s is missing a %s attribute",
122
username,myldap_get_dn(entry),attmap_passwd_uid);
125
/* check the username */
126
if ((value==NULL)||!isvalidname(value)||strlen(value)>=username_len)
128
log_log(LOG_WARNING,"passwd entry %s name denied by validnames option: \"%s\"",
129
myldap_get_dn(entry),username);
132
/* check if the username is different and update it if needed */
133
if (strcmp(username,value)!=0)
135
log_log(LOG_INFO,"username changed from \"%s\" to \"%s\"",username,value);
136
strcpy(username,value);
140
static int check_shadow(MYLDAP_SESSION *session,const char *username,
141
char *authzmsg,size_t authzmsgsz,
142
int check_maxdays,int check_mindays)
144
MYLDAP_ENTRY *entry=NULL;
145
long today,lastchangedate,mindays,maxdays,warndays,inactdays,expiredate;
147
long daysleft,inactleft;
148
/* get the shadow entry */
149
entry=shadow_uid2entry(session,username,NULL);
151
return NSLCD_PAM_SUCCESS; /* no shadow entry found, nothing to check */
152
/* get today's date */
153
today=(long)(time(NULL)/(60*60*24));
154
/* get shadown information */
155
get_shadow_properties(entry,&lastchangedate,&mindays,&maxdays,&warndays,
156
&inactdays,&expiredate,&flag);
157
/* check account expiry date */
158
if ((expiredate!=-1)&&(today>=expiredate))
160
daysleft=today-expiredate;
161
mysnprintf(authzmsg,authzmsgsz-1,"account expired %ld days ago",daysleft);
162
log_log(LOG_WARNING,"%s: %s",myldap_get_dn(entry),authzmsg);
163
return NSLCD_PAM_ACCT_EXPIRED;
165
/* password expiration isn't interesting at this point because the user
166
may not have authenticated with a password and if he did that would be
167
checked in the authc phase */
170
/* check lastchanged */
171
if (lastchangedate==0)
173
mysnprintf(authzmsg,authzmsgsz-1,"need a new password");
174
log_log(LOG_WARNING,"%s: %s",myldap_get_dn(entry),authzmsg);
175
return NSLCD_PAM_NEW_AUTHTOK_REQD;
177
else if (today<lastchangedate)
178
log_log(LOG_WARNING,"%s: password changed in the future",myldap_get_dn(entry));
179
else if (maxdays!=-1)
182
daysleft=lastchangedate+maxdays-today;
184
mysnprintf(authzmsg,authzmsgsz-1,"password will expire today");
186
mysnprintf(authzmsg,authzmsgsz-1,"password expired %ld days ago",-daysleft);
187
/* check inactdays */
188
if ((daysleft<=0)&&(inactdays!=-1))
190
inactleft=lastchangedate+maxdays+inactdays-today;
192
mysnprintf(authzmsg+strlen(authzmsg),authzmsgsz-strlen(authzmsg)-1,
193
", account will be locked today");
194
else if (inactleft>0)
195
mysnprintf(authzmsg+strlen(authzmsg),authzmsgsz-strlen(authzmsg)-1,
196
", account will be locked in %ld days",inactleft);
199
mysnprintf(authzmsg+strlen(authzmsg),authzmsgsz-strlen(authzmsg)-1,
200
", account locked %ld days ago",-inactleft);
201
log_log(LOG_WARNING,"%s: %s",myldap_get_dn(entry),authzmsg);
202
return NSLCD_PAM_AUTHTOK_EXPIRED;
207
/* log previously built message */
208
log_log(LOG_WARNING,"%s: %s",myldap_get_dn(entry),authzmsg);
209
return NSLCD_PAM_NEW_AUTHTOK_REQD;
212
if ((warndays>0)&&(daysleft<=warndays))
214
mysnprintf(authzmsg,authzmsgsz-1,"password will expire in %ld days",daysleft);
215
log_log(LOG_WARNING,"%s: %s",myldap_get_dn(entry),authzmsg);
221
daysleft=lastchangedate+mindays-today;
222
if ((mindays!=-1)&&(daysleft>0))
224
mysnprintf(authzmsg,authzmsgsz-1,"password cannot be changed for another %ld days",daysleft);
225
log_log(LOG_WARNING,"%s: %s",myldap_get_dn(entry),authzmsg);
226
return NSLCD_PAM_AUTHTOK_ERR;
229
return NSLCD_PAM_SUCCESS;
131
232
/* check authentication credentials of the user */
132
int nslcd_pam_authc(TFILE *fp,MYLDAP_SESSION *session)
233
int nslcd_pam_authc(TFILE *fp,MYLDAP_SESSION *session,uid_t calleruid)
134
235
int32_t tmpint32;
136
237
char username[256];
138
238
char servicename[64];
139
239
char password[64];
242
int authzrc=NSLCD_PAM_SUCCESS;
140
245
/* read request parameters */
141
246
READ_STRING(fp,username);
142
READ_STRING(fp,userdn);
247
SKIP_STRING(fp); /* DN */
143
248
READ_STRING(fp,servicename);
144
249
READ_STRING(fp,password);
146
log_log(LOG_DEBUG,"nslcd_pam_authc(\"%s\",\"%s\",\"%s\",\"%s\")",
147
username,userdn,servicename,*password?"***":"");
251
log_setrequest("authc=\"%s\"",username);
252
log_log(LOG_DEBUG,"nslcd_pam_authc(\"%s\",\"%s\",\"%s\")",
253
username,servicename,*password?"***":"");
148
254
/* write the response header */
149
255
WRITE_INT32(fp,NSLCD_VERSION);
150
256
WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHC);
152
258
authenticate as administrator, otherwise validate request as usual */
153
259
if ((*username=='\0')&&(nslcd_cfg->ldc_rootpwmoddn!=NULL))
155
if (strlen(nslcd_cfg->ldc_rootpwmoddn)>=sizeof(userdn))
157
log_log(LOG_ERR,"nslcd_pam_authc(): rootpwmoddn will not fit in userdn");
261
userdn=nslcd_cfg->ldc_rootpwmoddn;
262
/* if the caller is root we will allow the use of the rootpwmodpw option */
263
if ((*password=='\0')&&(calleruid==0)&&(nslcd_cfg->ldc_rootpwmodpw!=NULL))
265
if (strlen(nslcd_cfg->ldc_rootpwmodpw)>=sizeof(password))
267
log_log(LOG_ERR,"nslcd_pam_authc(): rootpwmodpw will not fit in password");
270
strcpy(password,nslcd_cfg->ldc_rootpwmodpw);
275
/* try normal authentication, lookup the user entry */
276
entry=validate_user(session,username,&rc);
279
/* for user not found we just say no result */
280
if (rc==LDAP_NO_SUCH_OBJECT)
282
WRITE_INT32(fp,NSLCD_RESULT_END);
160
strcpy(userdn,nslcd_cfg->ldc_rootpwmoddn);
162
else if (validate_user(session,userdn,sizeof(userdn),username,sizeof(username)))
164
WRITE_INT32(fp,NSLCD_RESULT_END);
286
userdn=myldap_get_dn(entry);
287
update_username(entry,username,sizeof(username));
167
289
/* try authentication */
168
290
rc=try_bind(userdn,password);
169
if (rc==NSLCD_PAM_SUCCESS)
291
if (rc==LDAP_SUCCESS)
170
292
log_log(LOG_DEBUG,"bind successful");
293
/* map result code */
296
case LDAP_SUCCESS: rc=NSLCD_PAM_SUCCESS; break;
297
case LDAP_INVALID_CREDENTIALS: rc=NSLCD_PAM_AUTH_ERR; break;
298
default: rc=NSLCD_PAM_AUTH_ERR;
300
/* perform shadow attribute checks */
302
authzrc=check_shadow(session,username,authzmsg,sizeof(authzmsg),1,0);
171
303
/* write response */
172
304
WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
173
305
WRITE_STRING(fp,username);
174
306
WRITE_STRING(fp,userdn);
175
WRITE_INT32(fp,rc); /* authc */
176
WRITE_INT32(fp,rc); /* authz */
177
WRITE_STRING(fp,""); /* authzmsg */
308
WRITE_INT32(fp,authzrc);
309
WRITE_STRING(fp,authzmsg);
178
310
WRITE_INT32(fp,NSLCD_RESULT_END);
228
360
element in the dict) */
231
static int try_autzsearch(MYLDAP_SESSION *session,DICT *dict,const char *searchfilter)
363
/* perform an authorisation search, returns an LDAP status code */
364
static int try_autzsearch(MYLDAP_SESSION *session,const char *dn,
365
const char *username,const char *servicename,
366
const char *ruser,const char *rhost,const char *tty)
368
char hostname[HOST_NAME_MAX+1];
233
371
char filter_buffer[1024];
234
372
MYLDAP_SEARCH *search;
235
373
MYLDAP_ENTRY *entry;
236
374
static const char *attrs[2];
377
/* check whether the search filter is configured at all */
378
if (!nslcd_cfg->ldc_pam_authz_search)
380
/* build the dictionary with variables
381
NOTE: any variables added here also need to be added to
382
cfg.c:parse_pam_authz_search_statement() */
384
autzsearch_var_add(dict,"username",username);
385
autzsearch_var_add(dict,"service",servicename);
386
autzsearch_var_add(dict,"ruser",ruser);
387
autzsearch_var_add(dict,"rhost",rhost);
388
autzsearch_var_add(dict,"tty",tty);
389
if (gethostname(hostname,sizeof(hostname))==0)
390
autzsearch_var_add(dict,"hostname",hostname);
391
if ((fqdn=getfqdn())!=NULL)
392
autzsearch_var_add(dict,"fqdn",fqdn);
393
autzsearch_var_add(dict,"dn",dn);
394
autzsearch_var_add(dict,"uid",username);
238
395
/* build the search filter */
239
if (expr_parse(searchfilter,filter_buffer,sizeof(filter_buffer),
240
autzsearch_var_get,(void *)dict)==NULL)
396
res=expr_parse(nslcd_cfg->ldc_pam_authz_search,
397
filter_buffer,sizeof(filter_buffer),
398
autzsearch_var_get,(void *)dict);
399
autzsearch_vars_free(dict);
242
log_log(LOG_ERR,"pam_authz_search \"%s\" is invalid",searchfilter);
403
log_log(LOG_ERR,"pam_authz_search \"%s\" is invalid",nslcd_cfg->ldc_pam_authz_search);
404
return LDAP_LOCAL_ERROR;
245
406
log_log(LOG_DEBUG,"trying pam_authz_search \"%s\"",filter_buffer);
246
407
/* perform the search */
254
415
log_log(LOG_ERR,"pam_authz_search \"%s\" failed: %s",
255
416
filter_buffer,ldap_err2string(rc));
258
419
/* try to get an entry */
259
entry=myldap_get_entry(search,NULL);
420
entry=myldap_get_entry(search,&rc);
262
423
log_log(LOG_ERR,"pam_authz_search \"%s\" found no matches",filter_buffer);
424
if (rc==LDAP_SUCCESS)
425
rc=LDAP_NO_SUCH_OBJECT;
265
428
log_log(LOG_DEBUG,"pam_authz_search found \"%s\"",myldap_get_dn(entry));
266
429
/* we've found an entry so it's OK */
270
433
/* check authorisation of the user */
271
434
int nslcd_pam_authz(TFILE *fp,MYLDAP_SESSION *session)
273
436
int32_t tmpint32;
274
438
char username[256];
276
439
char servicename[64];
280
char hostname[HOST_NAME_MAX+1];
440
char ruser[256],rhost[HOST_NAME_MAX+1],tty[64];
282
444
/* read request parameters */
283
445
READ_STRING(fp,username);
284
READ_STRING(fp,userdn);
446
SKIP_STRING(fp); /* DN */
285
447
READ_STRING(fp,servicename);
286
448
READ_STRING(fp,ruser);
287
449
READ_STRING(fp,rhost);
288
450
READ_STRING(fp,tty);
290
log_log(LOG_DEBUG,"nslcd_pam_authz(\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\")",
291
username,userdn,servicename,ruser,rhost,tty);
452
log_setrequest("authz=\"%s\"",username);
453
log_log(LOG_DEBUG,"nslcd_pam_authz(\"%s\",\"%s\",\"%s\",\"%s\",\"%s\")",
454
username,servicename,ruser,rhost,tty);
292
455
/* write the response header */
293
456
WRITE_INT32(fp,NSLCD_VERSION);
294
457
WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHZ);
295
/* validate request and fill in the blanks */
296
if (validate_user(session,userdn,sizeof(userdn),username,sizeof(username)))
298
WRITE_INT32(fp,NSLCD_RESULT_END);
301
if (nslcd_cfg->ldc_pam_authz_search)
303
/* TODO: perform any authorisation checks */
305
autzsearch_var_add(dict,"username",username);
306
autzsearch_var_add(dict,"service",servicename);
307
autzsearch_var_add(dict,"ruser",ruser);
308
autzsearch_var_add(dict,"rhost",rhost);
309
autzsearch_var_add(dict,"tty",tty);
310
if (gethostname(hostname,sizeof(hostname))==0)
311
autzsearch_var_add(dict,"hostname",hostname);
313
autzsearch_var_add(dict,"dn",userdn);
314
autzsearch_var_add(dict,"uid",username);
315
if (try_autzsearch(session,dict,nslcd_cfg->ldc_pam_authz_search))
458
/* validate request */
459
entry=validate_user(session,username,&rc);
462
/* for user not found we just say no result */
463
if (rc==LDAP_NO_SUCH_OBJECT)
317
WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
318
WRITE_STRING(fp,username);
319
WRITE_STRING(fp,userdn);
320
WRITE_INT32(fp,NSLCD_PAM_PERM_DENIED); /* authz */
321
WRITE_STRING(fp,"LDAP authorisation check failed"); /* authzmsg */
322
465
WRITE_INT32(fp,NSLCD_RESULT_END);
324
autzsearch_vars_free(dict);
469
/* check authorisation search */
470
rc=try_autzsearch(session,myldap_get_dn(entry),username,servicename,ruser,rhost,tty);
471
if (rc!=LDAP_SUCCESS)
473
WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
474
WRITE_STRING(fp,username);
476
WRITE_INT32(fp,NSLCD_PAM_PERM_DENIED);
477
WRITE_STRING(fp,"LDAP authorisation check failed");
478
WRITE_INT32(fp,NSLCD_RESULT_END);
481
/* perform shadow attribute checks */
482
rc=check_shadow(session,username,authzmsg,sizeof(authzmsg),0,0);
327
483
/* write response */
328
484
WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
329
485
WRITE_STRING(fp,username);
330
WRITE_STRING(fp,userdn);
331
WRITE_INT32(fp,NSLCD_PAM_SUCCESS); /* authz */
332
WRITE_STRING(fp,""); /* authzmsg */
486
WRITE_STRING(fp,myldap_get_dn(entry));
488
WRITE_STRING(fp,authzmsg);
333
489
WRITE_INT32(fp,NSLCD_RESULT_END);
427
int nslcd_pam_pwmod(TFILE *fp,MYLDAP_SESSION *session)
584
int nslcd_pam_pwmod(TFILE *fp,MYLDAP_SESSION *session,uid_t calleruid)
429
586
int32_t tmpint32;
430
588
char username[256];
431
589
char userdn[256];
432
591
char servicename[64];
433
592
char oldpassword[64];
434
593
char newpassword[64];
435
char *binddn=userdn; /* the user performing the modification */
594
const char *binddn=NULL; /* the user performing the modification */
437
598
/* read request parameters */
438
599
READ_STRING(fp,username);
439
READ_STRING(fp,userdn);
600
READ_STRING(fp,userdn); /* we can't ignore userdn for now here because we
601
need it to determine the modify-as-root case */
602
asroot=(nslcd_cfg->ldc_rootpwmoddn!=NULL)&&(strcmp(userdn,nslcd_cfg->ldc_rootpwmoddn)==0);
440
603
READ_STRING(fp,servicename);
441
604
READ_STRING(fp,oldpassword);
442
605
READ_STRING(fp,newpassword);
444
log_log(LOG_DEBUG,"nslcd_pam_pwmod(\"%s\",\"%s\",\"%s\",\"%s\",\"%s\")",
445
username,userdn,servicename,*oldpassword?"***":"",
607
log_setrequest("pwmod=\"%s\"",username);
608
log_log(LOG_DEBUG,"nslcd_pam_pwmod(\"%s\",%s,\"%s\",\"%s\",\"%s\")",
609
username,asroot?"asroot":"asuser",servicename,*oldpassword?"***":"",
446
610
*newpassword?"***":"");
447
611
/* write the response header */
448
612
WRITE_INT32(fp,NSLCD_VERSION);
449
613
WRITE_INT32(fp,NSLCD_ACTION_PAM_PWMOD);
614
/* validate request */
615
entry=validate_user(session,username,&rc);
618
/* for user not found we just say no result */
619
if (rc==LDAP_NO_SUCH_OBJECT)
621
WRITE_INT32(fp,NSLCD_RESULT_END);
450
625
/* check if the the user passed the rootpwmoddn */
451
if ((nslcd_cfg->ldc_rootpwmoddn!=NULL)&&(strcmp(userdn,nslcd_cfg->ldc_rootpwmoddn)==0))
453
628
binddn=nslcd_cfg->ldc_rootpwmoddn;
454
userdn[0]='\0'; /* cause validate_user() to get the user DN */
629
/* check if rootpwmodpw should be used */
630
if ((*oldpassword=='\0')&&(calleruid==0)&&(nslcd_cfg->ldc_rootpwmodpw!=NULL))
632
if (strlen(nslcd_cfg->ldc_rootpwmodpw)>=sizeof(oldpassword))
634
log_log(LOG_ERR,"nslcd_pam_pwmod(): rootpwmodpw will not fit in oldpassword");
637
strcpy(oldpassword,nslcd_cfg->ldc_rootpwmodpw);
456
/* validate request and fill in the blanks */
457
if (validate_user(session,userdn,sizeof(userdn),username,sizeof(username)))
459
WRITE_INT32(fp,NSLCD_RESULT_END);
642
binddn=myldap_get_dn(entry);
643
/* check whether shadow properties allow password change */
644
rc=check_shadow(session,username,authzmsg,sizeof(authzmsg),0,1);
645
if (rc!=NSLCD_PAM_SUCCESS)
647
WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
648
WRITE_STRING(fp,username);
651
WRITE_STRING(fp,authzmsg);
652
WRITE_INT32(fp,NSLCD_RESULT_END);
462
656
/* perform password modification */
463
rc=try_pwmod(binddn,userdn,oldpassword,newpassword);
657
rc=try_pwmod(binddn,myldap_get_dn(entry),oldpassword,newpassword);
658
if (rc!=LDAP_SUCCESS)
660
mysnprintf(authzmsg,sizeof(authzmsg)-1,"password change failed: %s",ldap_err2string(rc));
661
WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
662
WRITE_STRING(fp,username);
664
WRITE_INT32(fp,NSLCD_PAM_PERM_DENIED);
665
WRITE_STRING(fp,authzmsg);
666
WRITE_INT32(fp,NSLCD_RESULT_END);
464
668
/* write response */
465
669
WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
466
670
WRITE_STRING(fp,username);
467
WRITE_STRING(fp,userdn);
468
if (rc==LDAP_SUCCESS)
470
WRITE_INT32(fp,NSLCD_PAM_SUCCESS);
475
WRITE_INT32(fp,NSLCD_PAM_PERM_DENIED);
476
WRITE_STRING(fp,ldap_err2string(rc));
671
WRITE_STRING(fp,myldap_get_dn(entry));
672
WRITE_INT32(fp,NSLCD_PAM_SUCCESS);
478
674
WRITE_INT32(fp,NSLCD_RESULT_END);