NLM Issues and Strategies When Accessing NDS
Articles and Tips: article
Developer Support Engineer
Developer Support
MARK OBERG
Developer Support Engineer
Developer Support
01 Aug 1996
This article discusses how an NLM can deal with the extra security requirements introduced by NDS. NetWare security is discussed in the context of three major services accessed by NLMs-Bindery, File System, and Directory Services. The article shows what rights an NLM has to these services and why.
Introduction
You have been writing NLMs for quite awhile now and are feeling pretty confident. You have taken for granted the privilege that as long as your NLM is accessing the local file server's file system or NetWare 3.x Bindery, you seem to have unlimited power. Now it's time to try your hand at Directory Services. You see NWDSLogin() and NWDSAuthenticate() but you don't want to use this API because you still want to be the all powerful NLM and still not have to worry about a password during unattended NLM loading.
So you take the first piece of sample code you get your hands on, strip out the NWDSLogin() and NWDSAuthenticate() and compile. The code seems to be running OK until it gets to . . . -672 Error No Access . . . "Hey, how come the insufficient rights?"
If you're an NLM programmer in the 3.x environment, moving to 4.x and Novell Directory Services (NDS), the previous example demonstrates a typical misunderstanding of NLM rights in the NDS environment. Correcting this requires adjustments in NLM design.
This article discusses how your NLM can deal with the extra security requirements introduced by NDS. As an introduction we discuss fundamental security and rights concepts that apply to an NLM and the differences that exist in rights issues between the 3.x and the 4.x environments. Even though NDS has some rights issues which are different, it does offer NLMs additional benefits.
The sample code that is referenced in this article can be found on ftp.novell.com:pub\netwire\ndevsup\05\xdsa7.exe or CompuServe forum NDEVSUP, library 05, and file xdsa7.exe. To show the benefits of leveraging NDS in an NLM we demonstrate how to read NLM configuration information from this secure, persistent, centrally administered, and replicated database.
NetWare Security in a Nut Shell
Because the network facilitates the sharing of computer resources, NetWare security is responsible for regulating access to these shared resources. For security reasons, who you are determines what you are allowed to do - your RIGHTS. Since NetWare is not implemented as a single security system, different NetWare services use different security systems.
The following sections briefly discuss NetWare security in the context of three major services accessed by NLMs-Bindery, File System, and Directory Services-what rights your NLM has to these services and why. Also, additional special considerations affect the NLM's rights (i.e. is it a client or an extension of the server).
The NLM- Extension of the Server or a Client?
An NLM which is running on a NetWare server is considered a modular extension of the NetWare operating system, or server. Therefore, the NLM is trusted and not restricted to server resources. Within this server-centered environment, the NLM is considered part of the server and therefore is all powerful, having Supervisor equivalent rights.
As an example, the Bindery and File System are systems which are "server" centered resources, complete systems on the server, and considered part of the server. Therefore, when the NLM is accessing the local Bindery or local File System it has all rights. The NLM is considered running on the "local" server; therefore, the NLM is acting as an extension of the server it's running on and the NLM has Supervisor equivalent rights on this server. Whenever the NLM needs to access a Bindery or File System which doesn't belong to the local server it is accessing a "remote" server on a different physical machine. In this situation, the NLM is no longer an extension of the local server but becomes a "client" of the "remote" server.
When first establishing a connection the client NLM has limited rights until it identifies itself as a valid user. For example, in the case of remote Bindery access, when your NLM first becomes a client by attaching to the remote host, it has limited read access to the Bindery until it identifies itself as a valid object by logging into the remote server. The remote File System requires the client to establish its object ID so it can perform client access control.
NDS adds an additional security system which is networked based instead of server based, requiring new security APIs you will need to be aware of and use. However, the security systems for server-based resources are still in place and your NLM will still have to identify itself to them before being able to access them.(For a 3.x NLM remote client to identity itself for increased rights to the 3.x server with a Bindery or a 4.x server with bindery emulation and for File System-and other server centric resources-access the client can use the APIs AttachToFileServer() and LoginToFileSever(). A 4.x NLM remote client accessing these 3.x resources would use these same APIs.)
However, in the case of the 4.x remote client accessing the 4.x server's File System and other server-based resources (such as Queue Management Services-QMS), it would first use NWDSLogin() to authenticate into Directory Services, thus establishing an Object ID other than [Public], and then use NWDSAuthenticate() to authenticate (bind) to the file server.
"Come on. My NLM is running on the local server, not accessing a remote server. How come my NLM doesn't seem to have SUPERVISOR rights when I access DS? I'm not going remote."
Directory Services is a "network" centered resource, often spanning multiple servers as one entity. Therefore Directory Services does not belong to one server; your NLM is just considered another client to DS. To be able to access the network to the fullest, your NLM must identify itself as an authentic client.
When your NLM first attaches to the directory, this attached client receives [Public] access to the Directory, which only gives it browse rights to the directory. Only after performing an NWDSLogin() will your NLM establish its credentials, therefore increasing its rights to the directory by authenticating to the directory.
Authentications Between Loads
"OK, so I don't have immediate Supervisor rights when accessing the Directory Services. Where am I going to store the password if my NLM is going to do unattended logins when the server is brought up? If my NLM only has [Public] rights which just allows my NLM rights to browse the Directory then I'm not going to be able to read a password attribute from the Directory."
One of the issues with having NLMs log in to NDS is that NLMs are required to load without administrator intervention when file servers are taken down and back up. Until the NLM authenticates to the Directory, it only has browse rights as object [Public]. We now demonstrate how your NLM can access Directory Services with persistent authentication.
An NLM on 4.x NetWare still has Supervisor access to the local file system. Using this access right, an NLM can store a password in a configuration file in a fairly secure place where Supervisor equivalence would be required. As an additional security measure, to minimize the risk of tampering by other administrators you could perform minimal encryption on the password.
The functions GetPasswordFromSys() and StorePasswordToSys() are integrated with NWDSLogin() to demonstrate this functionality around line number 727 of EXAMPSRV.C.
Benefits of an NLM Authenticating to NDS
Three benefits of using the functionality of NDS for NLM services are leverageable by having NLMs log in to the directory:
The NLMs receive a global identity for the service or services that they provide and act as a service location mechanism.
NLMs can use NDS and background authentication to access resources external to the local NetWare file system. The NLM is required to supply a password once-when it uses NWDSLogin() to authenticate to Directory Services-then background authentication during NWDSAuthenticate() does not require an NLM to pass another password. This greatly enhances security.
NLMs that log in as their identity can store global configuration or local specific configuration attributes in NDS to provide a fault tolerant configuration storage medium. This results in a service being able to have a globally accessible configuration store, represented as the NDS service object. Along with this storage, the rights to the configuration can be limited to read access for the NLM service, reserving modification rights for the administrator. This reduces the risk of sabotage from others.
Around line 563 in the example code, a description is stored when a service object is created. This represents a service configuration which is read with the GetDescription() function when the service comes up around line 826.
1 /**************************************************************************** 2 ** File: exampsrv.c 3 ** 4 ** Description: 5 ** This example server demonstrates one way to provide an NLM NDS 6 ** authentication between NLM loads. The NLM's identity in this 7 ** example is passed on the command line. The authentication is 8 ** stored securely in the local file system with measures to protect 9 ** it from other administrators. 10 ** 11 ** 12 ** Program flow: 13 ** 1. Check for NLM identity on command line, if not present display usage. 14 ** 2. Call NWDSReadObjectInfo() to determine if object exists. If object 15 ** doesn't exist prompt is new service object is to be created. 16 ** Create service object prompting for parameters if required. Store 17 ** password for service object in sys:system\exampsrv.cfg 18 ** 3. Look for a password in sys:system\exampsrv.cfg. If found, try 19 ** NWDSLogin(). If receive 669 instruct that the stored password is 20 ** invalid and prompt for different password or allow to exit. 21 ** 3. Once logged in, store any password changes in sys:system\exampsrv.cfg 22 ** 4. Read the local host servers DN and compare it with the service 23 ** object's DN. If different attempt to change service object location 24 ** if receive an error changing service location, fail because 25 ** administrator may specify to not let the NLM load anywhere else 26 ** through this mechanism using ACL assignment restricting rights 27 ** for the service object to change the "Host Server" attribute." 28 ** 5. Read the NLM service "Description" attribute and display it on the" 29 ** NLM screen to demonstrate an NLM getting it's configuration from 30 ** the directory. 31 ** 6. Set the status attribute to UP. 32 ** 7. Display shutdown option. 33 ** 8. During shutdown, set the status attribute to DOWN. 34 ** 35 ** Disclaimer: 36 ** 37 ** Novell, Inc. makes no representations or warranties with respect to 38 ** any NetWare software, and specifically disclaims any express or 39 ** implied warranties of merchantability, title, or fitness for a 40 ** particular purpose. 41 ** 42 ** Distribution of any NetWare software is forbidden without the 43 ** express written consent of Novell, Inc. Further, Novell reserves 44 ** the right to discontinue distribution of any NetWare software. 45 ** 46 ** Novell is not responsible for lost profits or revenue, loss of use 47 ** of the software, loss of data, costs of re creating lost data, the 48 ** cost of any substitute equipment or program, or claims by any party 49 ** other than you. Novell strongly recommends a backup be made before 50 ** any software is installed. Technical support for this software 51 ** may be provided at the discretion of Novell. 52 ** 53 ** Programmers: 54 ** 55 ** Ini Who Firm 56 ** 57 ** MDO Mark Dale Oberg Novell Developer Services Division 58 ** 59 */ 60 61 /**************************************************************************** 62 ** Include headers, macros, structures, typedefs, etc. 63 */ 64 #include <stdio.h<< 65 #include <malloc.h<< 66 #include <string.h<< 67 #include <conio.h<< 68 #include <process.h<< 69 #include <sys/types.h<< 70 #include <fcntl.h<< 71 #include <niterror.h<< 72 #include <nwdsapi.h<< 73 #include <nwenvrn.h<< 74 #include <signal.h<< 75 #include <nwcntask.h<< 76 #include <errno.h<< 77 #include <ctype.h<< 78 #include "dsaccess.h"" 79 80 /* 81 ** Macro definitions 82 */ 83 84 #define PASSWORD_SIZE 100 85 86 /************************************************************************* 87 ** ExitWithDisplayUsage 88 */ 89 void ExitWithDisplayUsage(void) 90 { 91 printf("Syntax: load exampsrv "service object name"\n");" 92 printf("Example:load exampsrv exampsrv.devsup.services.novell\n");" 93 exit(0); 94 } 95 96 /***************************************************************************** 97 ** GetPassword prompts the user with "Password:" and accepts a string marking" 98 ** each entered position with an asterisk. if an <ESC< is entered a EFAILURE is< 99 ** returned, otherwise ESUCCESS is returned. 100 */ 101 uint32 GetPassword(char *password, int size) 102 { 103 int index=0; 104 WORD x,y; 105 106 printf("Password: ");" 107 y=wherey(); /* find current row */ 108 109 do 110 { 111 x=wherex(); /* find current column */ 112 password[index]=getch(); 113 if(0x0D==password[index]) 114 { 115 password[index]='\0'; 116 } 117 else if (0x1B==password[index]) /* detect esc */ 118 { 119 password[0]='\0'; 120 return(EFAILURE); 121 } 122 else if(0x08==password[index] && 0&=index&1) /* Backspace */& 123 { 124 index =2; 125 gotoxy(x 1,y); /* backup */ 126 putch(' '); /* clear output */ 127 gotoxy(x 1,y); /* backup */ 128 } 129 else 130 putch('*'); 131 132 index++; 133 x++; /* follow cursor position */ 134 }while (password[index 1] index size); 135 136 if(index >= size)> 137 { 138 password[0]='\0'; 139 return(EFAILURE); 140 } 141 142 putch('\n'); 143 return(ESUCCESS); 144 } 145 146 147 /***************************************************************************** 148 ** StorePasswordToSys() writes the provided password string to sys:system\ 149 ** exampsrv.cfg. This file is used to store the NLM password between loads 150 ** in a secure area on the sys:system volume. It may be wise to modify 151 ** this routine to encrypt the password file from plain text to protect the 152 ** service object from administrators that have access to sys:system. 153 */ 154 uint32 StorePasswordToSys(char *password) 155 { 156 FILE *fp; 157 int length; 158 159 fp=fopen("sys:\\system\\exampsrv.cfg", "wb");" 160 if(NULL == fp) 161 { 162 return ( 1); 163 } 164 165 length = fwrite(password, sizeof(char), PASSWORD_SIZE, fp); 166 if(PASSWORD_SIZE != length) 167 { 168 fclose(fp); 169 return( 1); 170 } 171 172 fclose(fp); 173 return(0); 174 } 175 176 /**************************************************************************** 177 ** GetPasswordFromSys() looks for the file sys:system\exampsrv.cfg and 178 ** attempts to read a stored password from the file. 179 */ 180 uint32 GetPasswordFromSys(char *password) 181 { 182 FILE *fp; 183 int length; 184 185 fp=fopen("sys:\\system\\exampsrv.cfg", "rb");" 186 if(NULL == fp) 187 { 188 return ( 1); 189 } 190 191 length = fread(password, sizeof(char), PASSWORD_SIZE, fp); 192 if(PASSWORD_SIZE != length) 193 { 194 fclose(fp); 195 return( 1); 196 } 197 198 fclose(fp); 199 return(0); 200 } 201 202 /**************************************************************************** 203 ** GetHostName reads the "Host Server" attribute for the provided object" 204 */ 205 uint32 GetHostName(NWDSContextHandle cx, char *hostName, char *serverDN) 206 { 207 uint32 ccode; 208 DS_HANDLE *dsHandle; 209 READ_DATA *readData; 210 char *attrNames[]={"Host Server",NULL};" 211 212 /* 213 ** Opens a dsHandle to use for reading the "Host Server" value" 214 */ 215 dsHandle=DSReadOpen(cx, attrNames); 216 if(NULL==dsHandle) 217 { 218 printf("Failed DSReadOpen.\n");" 219 ccode=ERR_NOT_ENOUGH_MEMORY; 220 goto Exit0; 221 } 222 223 /* 224 ** Initiates the read 225 */ 226 ccode=DSReadSeek( 227 /*DS_HANDLE *readHandle,*/ dsHandle, 228 /*char *baseObjectName, */ serverDN, /*read first object found on search */ 229 /*NWDS_TYPE infoType ) */ 1 ); /* attribute names and values */ 230 if(ccode) 231 { 232 printf("Failed DSReadSeek During GetHostName: %d\n",ccode);" 233 goto Exit1; 234 } 235 236 /* 237 ** DSRead returns data until EOF is returned. 238 */ 239 for(readData=DSRead(dsHandle);NULL !=readData && EOF != readData;readData=DSRead(dsHandle))& 240 { 241 switch (readData readType) 242 { 243 case ATTR: /* will be "Host Server" */" 244 break; 245 246 case ATTR_VALUE: 247 strcpy(hostName, (char *)readData read.attrVal.value); 248 } 249 free(readData); 250 } 251 252 if(NULL==readData) 253 { 254 printf("Failed DSRead.\n");" 255 } 256 257 /* 258 ** close the handle. 259 */ 260 Exit1: 261 DSClose(dsHandle); /* deallocate resources from DSSearchOpen */ 262 Exit0: 263 return (ccode); 264 } 265 266 /**************************************************************************** 267 ** GetDescription reads the "Description" attribute for the provided object" 268 */ 269 uint32 GetDescription(NWDSContextHandle cx, char *description, char *serverDN) 270 { 271 uint32 ccode; 272 DS_HANDLE *dsHandle; 273 READ_DATA *readData; 274 char *attrNames[]={"Description",NULL};" 275 276 /* 277 ** Opens a dsHandle to use for reading the "Host Server" value" 278 */ 279 dsHandle=DSReadOpen(cx, attrNames); 280 if(NULL==dsHandle) 281 { 282 printf("Failed DSReadOpen.\n");" 283 ccode=ERR_NOT_ENOUGH_MEMORY; 284 goto Exit0; 285 } 286 287 /* 288 ** Initiates the read 289 */ 290 ccode=DSReadSeek( 291 /*DS_HANDLE *readHandle,*/ dsHandle, 292 /*char *baseObjectName, */ serverDN, /*read first object found on search */ 293 /*NWDS_TYPE infoType ) */ 1 ); /* attribute names and values */ 294 if(ccode) 295 { 296 printf("Failed DSReadSeek: %d\n",ccode);" 297 goto Exit1; 298 } 299 300 /* 301 ** DSRead returns data until EOF is returned. 302 */ 303 for(readData=DSRead(dsHandle);NULL !=readData && EOF != readData;readData=DSRead(dsHandle))& 304 { 305 switch (readData readType) 306 { 307 case ATTR: /* will be "Description" */" 308 break; 309 310 case ATTR_VALUE: 311 strcpy(description, (char *)readData read.attrVal.value); 312 } 313 free(readData); 314 } 315 316 if(NULL==readData) 317 { 318 printf("Failed DSRead.\n");" 319 } 320 321 /* 322 ** close the handle. 323 */ 324 Exit1: 325 DSClose(dsHandle); /* deallocate resources from DSSearchOpen */ 326 Exit0: 327 return (ccode); 328 } 329 330 331 /**************************************************************************** 332 ** SetHostName sets the objects host name 333 */ 334 NWDSCCODE SetHostName(NWDSContextHandle context, char *serverDN, char *hostName) 335 { 336 NWDSCCODE ccode=0; 337 Buf_T *modifyBuf; 338 int32 iterationHandle = NO_MORE_ITERATIONS; 339 340 ccode=NWDSAllocBuf(DEFAULT_MESSAGE_LEN, &modifyBuf);& 341 if(ccode) 342 { 343 goto Exit0; 344 } 345 346 ccode = NWDSInitBuf(context, DSV_MODIFY_ENTRY, modifyBuf); 347 if(ccode) 348 { 349 goto Exit1; 350 } 351 352 ccode = NWDSPutChange(context, modifyBuf, DS_REMOVE_ATTRIBUTE, "Host Server");" 353 if(ccode) 354 { 355 goto Exit1; 356 } 357 358 ccode = NWDSPutChange(context, modifyBuf, DS_ADD_VALUE, "Host Server");" 359 if(ccode) 360 { 361 goto Exit1; 362 } 363 364 ccode = NWDSPutAttrVal(context, modifyBuf, SYN_DIST_NAME, hostName); 365 if(ccode) 366 { 367 goto Exit1; 368 } 369 370 ccode = NWDSModifyObject(context, 371 serverDN, 372 &iterationHandle,& 373 0, 374 modifyBuf); 375 Exit1: 376 NWDSFreeBuf(modifyBuf); 377 Exit0: 378 return(ccode); 379 } 380 381 382 /**************************************************************************** 383 ** updateFlag() sets the status flag and handles return codes. 384 */ 385 NWDSCCODE updateFlag(NWDSContextHandle context, char *serverDN, Integer_T flag) 386 { 387 NWDSCCODE ccode=0; 388 Buf_T *modifyBuf; 389 int32 iterationHandle = NO_MORE_ITERATIONS; 390 391 ccode=NWDSAllocBuf(DEFAULT_MESSAGE_LEN, &modifyBuf);& 392 if(ccode) 393 { 394 return(ccode); 395 } 396 397 ccode = NWDSInitBuf(context, DSV_MODIFY_ENTRY, modifyBuf); 398 if(ccode) 399 { 400 goto Exit1; 401 } 402 ccode = NWDSPutChange(context, modifyBuf, DS_REMOVE_ATTRIBUTE, "Status");" 403 404 if(ccode) 405 { 406 goto Exit1; 407 } 408 409 ccode = NWDSPutChange(context, modifyBuf, DS_ADD_VALUE, "Status");" 410 if(ccode) 411 { 412 goto Exit1; 413 } 414 415 ccode = NWDSPutAttrVal(context, modifyBuf, SYN_INTEGER, &flag);& 416 if(ccode) 417 { 418 goto Exit1; 419 } 420 421 ccode = NWDSModifyObject(context, 422 serverDN, 423 &iterationHandle,& 424 0, 425 modifyBuf); 426 427 Exit1: 428 NWDSFreeBuf(modifyBuf); 429 return(ccode); 430 } 431 432 /**************************************************************************** 433 ** SetStatusUp() modifies the status attribute of the object to be UP. 434 */ 435 uint32 SetStatusUp(uint32 cx, char *object) 436 { 437 return updateFlag(cx, object, TRUE); 438 } 439 440 /**************************************************************************** 441 ** SetStatusDown() modifies the status attribute of the object to be DOWN. 442 */ 443 uint32 SetStatusDown(uint32 cx, char *object) 444 { 445 return updateFlag(cx, object, FALSE); 446 } 447 448 /**************************************************************************** 449 ** GetLocalHostName gets the local servers DN. 450 */ 451 uint32 GetLocalHostName(NWDSContextHandle cx, char *hostName) 452 { 453 uint32 ccode; 454 LONG connection; 455 456 SetCurrentConnection( 1); /* get a non zero connection to the local host*/ 457 connection=GetCurrentConnection(); 458 459 ccode=NWDSGetServerDN(cx, (int)connection, (char *)hostName); 460 if(ccode) 461 { 462 printf("Error during NWDSGetServerDN()");" 463 } 464 465 ReturnConnection(connection); 466 return (ccode); 467 } 468 469 /**************************************************************************** 470 ** CreateServiceObject() allows exampsrv.nlm to create a new configuration 471 ** at load time. It prompts the user to login as an administrator, provide 472 ** configuration information and then stores the configuration in DS. 473 ** After storing the information a key pair is generated with a provided 474 ** password for the NLM service. The password is stored in sys:system\ 475 ** exampsrv.cfg 476 */ 477 uint32 CreateServiceObject(NWDSContextHandle cx, char *serviceDN) 478 { 479 NWDSCCODE ccode; 480 Buf_T*addBuffer; 481 NWDS_ITERATION iteration=NO_MORE_ITERATIONS; 482 char description[250]="Default configuration";" 483 char administrator[MAX_DN_CHARS+1]; 484 char hostName[MAX_DN_CHARS+1]; 485 char password[100+1]; 486 char verifyPassword[100+1]; 487 Integer_T status=FALSE; 488 489 /* 490 ** Prompt to login as an administrator that can create the service 491 ** object. 492 */ 493 printf("Enter administrator name to create object: ");" 494 gets(administrator); 495 GetPassword(password, 100); 496 497 /* 498 ** Login as the administrator 499 */ 500 ccode=NWDSLogin( 501 /* NWDSContextHandle context, */ cx, 502 /* uint32 optionsFlag, */ 0, 503 /* char NWFAR *objectName, */ administrator, 504 /* char NWFAR *password, */ password, 505 /* uint32 validityPeriod */ 0 ); 506 if(ccode) 507 { 508 printf("Login failed. Please check administrator name or password and try again.\n");" 509 goto Exit0; 510 } 511 512 /* 513 ** Get and verify a password to be associated with the server object. 514 */ 515 516 printf("Enter in a password to be associated with the service object.\n");" 517 printf("This password will be the password that is used to authenticate\n");" 518 printf("the service NLM assocaited with this service object.\n");" 519 520 do{ 521 ccode=GetPassword(password,100); 522 if(ccode) 523 { 524 goto Exit1; /* logout and return */ 525 } 526 printf("\n\nPlease retype the password to ensure it was typed correctly.\n");" 527 ccode=GetPassword(verifyPassword,100); 528 if(ccode) 529 { 530 goto Exit1; /* logout and return */ 531 } 532 533 if(strcmp(password, verifyPassword)) 534 { 535 printf("The password was entered incorrectly. Please try again or press "ESC" to exit.");" 536 ccode=EFAILURE; 537 } 538 }while(ccode); 539 540 541 /* 542 ** Get service description from user. 543 */ 544 printf("Enter service description. This is representative of a service configuration.\n");" 545 gets(description); 546 if(!strlen(description)) 547 { 548 strcpy(description,"Default Description");" 549 } 550 551 /* 552 ** interrogate the local server for a host name. 553 */ 554 ccode=GetLocalHostName(cx, hostName); 555 if(ccode) 556 { 557 printf("Error GetLocalHostName(): %d\n",ccode);" 558 goto Exit1; 559 } 560 561 /* 562 ** Proceed to create the service object. 563 */ 564 ccode=NWDSAllocBuf(DEFAULT_MESSAGE_LEN, &addBuffer);& 565 if(ccode) 566 { 567 printf("Error NWDSAllocBuf().\n");" 568 goto Exit1; 569 } 570 571 ccode=NWDSInitBuf(cx, DSV_ADD_ENTRY, addBuffer); 572 if(ccode) 573 { 574 printf("Error NWDSInitBuf()\n");" 575 goto Exit2; 576 } 577 578 ccode=NWDSPutAttrName(cx, addBuffer, "Object Class");" 579 if(ccode) 580 { 581 printf("Error NWDSPutAttrName()\n");" 582 goto Exit2; 583 } 584 585 ccode=NWDSPutAttrVal(cx, addBuffer, SYN_CLASS_NAME, "DEVSUP:ExampleNLMService");" 586 if(ccode) 587 { 588 printf("Error NWDSPutAttrVal()\n");" 589 goto Exit2; 590 } 591 592 ccode=NWDSPutAttrName(cx, addBuffer, "Description");" 593 if(ccode) 594 { 595 printf("Error NWDSPutAttrName()\n");" 596 goto Exit2; 597 } 598 599 ccode=NWDSPutAttrVal(cx, addBuffer, SYN_CI_STRING, description); 600 if(ccode) 601 { 602 printf("Error NWDSPutAttrVal()\n");" 603 goto Exit2; 604 } 605 606 ccode=NWDSPutAttrName(cx, addBuffer, "Host Server");" 607 if(ccode) 608 { 609 printf("Error NWDSPutAttrName()\n");" 610 goto Exit2; 611 } 612 613 ccode=NWDSPutAttrVal(cx, addBuffer, SYN_DIST_NAME, hostName); 614 if(ccode) 615 { 616 printf("Error NWDSPutAttrVal()\n");" 617 goto Exit2; 618 } 619 620 ccode=NWDSPutAttrName(cx, addBuffer, "Status");" 621 if(ccode) 622 { 623 printf("Error NWDSPutAttrName()\n");" 624 goto Exit2; 625 } 626 627 ccode=NWDSPutAttrVal(cx, addBuffer, SYN_INTEGER, (void *)&status);& 628 if(ccode) 629 { 630 printf("Error NWDSPutAttrVal()\n");" 631 goto Exit2; 632 } 633 634 ccode=NWDSAddObject(cx, serviceDN, &iteration, 0, addBuffer);& 635 if(ccode) 636 { 637 printf("Error: NWDSAddObject(, %s,):%d",serviceDN, ccode);" 638 goto Exit2; 639 } 640 641 /* 642 ** Associate an object key pair generated with the provided password 643 */ 644 ccode=NWDSGenerateObjectKeyPair(cx, serviceDN, password, 0); 645 if(ccode) 646 { 647 printf("Error NWDSGenerateObjectKeyPair(%s): %d",serviceDN,ccode);" 648 goto Exit2; 649 } 650 651 /* 652 ** Store the service object password so that it comes up automatically 653 ** next time. 654 */ 655 ccode=StorePasswordToSys(password); 656 if(ccode) 657 { 658 printf("password storage failed.");" 659 } 660 661 Exit2: 662 NWDSFreeBuf(addBuffer); 663 Exit1: 664 NWDSLogout(cx); 665 Exit0: 666 return(ccode); 667 } 668 669 /**************************************************************************** 670 ** Program flow is fully contained in main. Procedures are used to 671 ** encapsulate implementation details. 672 */ 673 void main(int argc, char *argv[]) 674 { 675 uint32 passwordFromFile=FALSE; 676 NWDSContextHandle cx; 677 NWDSCCODE ccode; 678 uint32 flags; 679 char password[255+1]; 680 char description[255+1]; 681 char dn[MAX_DN_CHARS+1]; 682 char hostName[MAX_DN_CHARS+1]; 683 char localHostName[MAX_DN_CHARS+1]; 684 Object_Info_T objectInfo; 685 char key; 686 687 /* 688 ** Check command line args. 689 */ 690 if(argc != 2) 691 ExitWithDisplayUsage(); 692 693 /* 694 ** Create a context for ds calls to use 695 */ 696 cx=NWDSCreateContext(); 697 if(ERR_CONTEXT_CREATION == cx) /* error if negative value */ 698 { 699 printf("Error creating context.\n");" 700 exit(0); 701 } 702 703 /* 704 ** Clear the canonicalize flag so that all NDS names are reqiored to be 705 ** full distinguished names relative to [Root]. 706 */ 707 ccode=NWDSGetContext(cx, DCK_FLAGS, &flags);& 708 if(ccode) 709 { 710 printf("Error NWDSGetContext(): %d\n",ccode);" 711 goto Exit1; 712 } 713 714 flags ^= DCV_CANONICALIZE_NAMES; 715 716 ccode=NWDSSetContext(cx, DCK_FLAGS, &flags);& 717 if(ccode) 718 { 719 printf("Error NWDSSetContext(): %d\n",ccode);" 720 goto Exit1; 721 } 722 723 /* 724 ** Does the specified service object exist? 725 */ 726 ccode=NWDSReadObjectInfo(cx, argv[1], dn, &objectInfo);& 727 if( 601==ccode) 728 { 729 printf("The provided object \"%s\" does not exist. Would you like to\n",argv[1]);" 730 printf("create this service object? (Y/N)");" 731 key=toupper(getch()); 732 putch('\n'); 733 if(key == 'Y') 734 { 735 ccode=CreateServiceObject(cx, argv[1]); /* create the service object */ 736 if(EFAILURE==ccode) 737 { 738 printf("Shuting down without creating service object.\n");" 739 goto Exit1; 740 } 741 else if(ccode) 742 { 743 printf("serviceObject creation failed.\n");" 744 goto Exit1; 745 } 746 } 747 else if(ccode !=0) 748 { 749 goto Exit1; /* free context */ 750 } 751 } 752 753 /* 754 ** Try getting password from sys:system, if not prompt for a valid password 755 */ 756 ccode=GetPasswordFromSys(password); /* check for password in sys:system */ 757 if(ccode) 758 { 759 passwordFromFile=FALSE; 760 printf("Enter a password for %s.\n",argv[1]);" 761 GetPassword(password,255); /* prompt for password at command line */ 762 } 763 else 764 { 765 passwordFromFile=TRUE; 766 } 767 768 /* 769 ** Try logging in. If 669 prompt for password. If 601, prompt if a 770 ** new service object is to be created. 771 */ 772 ccode=NWDSLogin( 773 /* NWDSContextHandle context, */ cx, 774 /* uint32 optionsFlag, */ 0, 775 /* char NWFAR *objectName, */ argv[1], 776 /* char NWFAR *password, */ password, 777 /* uint32 validityPeriod */ 0 ); 778 779 switch(ccode) 780 { 781 /* password was incorrect, if from file allow for update */ 782 case 669: 783 if(passwordFromFile==TRUE) 784 { 785 printf("The stored password for %s is invalid. Please enter the\n",argv[1]);" 786 printf("correct password to update the stored password.\n");" 787 GetPassword(password,255); /* prompt user for a password */ 788 ccode=NWDSLogin( 789 /* NWDSContextHandle context, */ cx, 790 /* uint32 optionsFlag, */ 0, 791 /* char NWFAR *objectName, */ argv[1], 792 /* char NWFAR *password, */ password, 793 /* uint32 validityPeriod */ 0 ); 794 if(ccode) 795 { 796 printf("Login failed. Please check service object name provided\n");" 797 printf("on the load line and password.\n");" 798 goto Exit1; 799 } 800 else 801 { 802 ccode=StorePasswordToSys(password); 803 if(ccode) 804 { 805 printf("Failure to store password.");" 806 } 807 } 808 } 809 else 810 { 811 printf("The password you provided is incorrect. Service load failed.\n");" 812 goto Exit1; 813 } 814 break; 815 816 case 0: /* success */ 817 break; 818 default: 819 printf("NWDSLogin failure ccode: %d\n",ccode);" 820 goto Exit1; 821 } 822 823 /* 824 ** Determine if loading on same host. 825 */ 826 GetLocalHostName(cx, localHostName); 827 828 GetHostName(cx, hostName, argv[1]); 829 830 if(strcmp(localHostName, hostName)) 831 { 832 SetHostName(cx, hostName, argv[1]); 833 } 834 835 /* 836 ** Set the status attribute to UP. 837 */ 838 ccode=SetStatusUp(cx,argv[1]); 839 if(ccode) 840 { 841 printf("SetStatusUp() failed %d.\n",ccode);" 842 goto Exit2; 843 } 844 845 /* 846 ** Read the NLM service "Description" attribute and display it on the" 847 ** NLM screen to demonstrate an NLM getting it's configuration from 848 ** the directory. 849 */ 850 ccode=GetDescription(cx, description, argv[1]); 851 if(ccode) 852 { 853 printf("Error during GetDescription(): %d\n",ccode);" 854 goto Exit2; 855 } 856 857 clrscr(); 858 printf("EXAMPSRV service is running as %s with a\n",argv[1]);" 859 printf("Description: %s\n",description);" 860 861 /* 862 ** Display shutdown option, wait for NLM user to shutdown. Also register 863 ** for the down server event. 864 */ 865 printf("Press any key to shutdown");" 866 while(!kbhit()) 867 { 868 delay(1); 869 } 870 871 /* 872 ** Set the status attribute to DOWN. 873 */ 874 ccode=SetStatusDown(cx,argv[1]); 875 if(ccode) 876 { 877 printf("SetStatusDown() failed %d.\n",ccode);" 878 } 879 880 Exit2: 881 NWDSLogout(cx); 882 Exit1: 883 NWDSFreeContext(cx); 884 }
* Originally published in Novell AppNotes
Disclaimer
The origin of this information may be internal or external to Novell. While Novell makes all reasonable efforts to verify this information, Novell does not make explicit or implied claims to its validity.