Novell Home

NDS102: Authenticating to NDS Using C (NDAP) APIs

Articles and Tips: article

LAWRENCE V. FISHER
Senior Research Engineer
Developer Information

01 Feb 1999


This lesson in DeveloperNet University's NDS category provides you with the information necessary to write a client-side C application that uses NDAP to establish an authenticated connection to an NDS tree.

Introduction

This is another code example in DeveloperNet University's NDS category. The information below describes what you will need in order to build its sample application.


Objective
Using this lesson as a guide, a C/C++ programmer will be able to use Novell's Directory Access Protocol (NDAP) Client32 API libraries to build a Win32 client-side-only application that can obtain an authenticated connection to an NDS directory.

Prerequisites

Entry level C programming skills.

Required Items

A Win32 development environment. The Novell Libraries for C and documentation installed. Novell's Libraries for C install is available along with a free DeveloperNet subscription from: http://developer.novell.com/ndk/

The downloaded libraries will also include libraries for Novell server-side development (NLMs). This example is concerned only with client-side development, so ignore the NLM materials completely.

Optional Items

Novell's NetWare Administrator or ConsoleOne NDS Administration tools are available along with a free DeveloperNet subscription from: http://developer.novell.com/ndk/

Required Setup

A valid user object on at least one NDS tree.

The purpose of this lesson is to provide you with the information necessary to write a client-side C application that uses NDAP to establish an authenticated connection to an NDS tree. This lesson has been divided into the following sections:

  • NDS102 GUI

  • NetWare Connections

  • NetWare Name Resolution

  • Primary Authentication

  • Connection States

  • Obtaining an Authenticated Client-side Connection

  • Background Authentication

  • Security Terms

  • NetWare's Primary Authentication Protocol

  • NetWare's Server-side Background Authentication Protocol

  • Source Code

The NDS102 GUI

Figure 1 shows the GUI provided for NDS102 on how to use Novell Directory Access Protocol (NDAP) APIs. The GUI is basically the same as it was in NDS101 except that in NDS101, an attribute value was read from the tree over a pre-existing authenticated connection. This lesson will show you how to establish an authenticated connection of your own before the attribute value is read.

Figure 1: The GUI for NDS102 using NDAP APIs.

Click the "Read Attribute" button, to have the GUI code execute the example code and establish a connection authenticated for the user specified by "objectName" located in the directory specified by "treeName." The getFieldStringValue( ) function from the NDS101 NDAP example is then called to read the specified attribute.

Figure 2 shows a code snippet that can be used to obtain an authenticated connection to NDS. As you can see, not a lot of code is necessary to obtain an authenticated connection to NDS using NDAP.

Figure 2: You can modify the example code so that the GUI will call your experiments.

NetWare Connections

Network connections make it possible for computers to talk to each other over a network. Programmers see a network connection as a body of information stored on a network platform which describes a session of communication with another platform. Very simply put, each side gives the session information its own unique ID. Packets coming over the wire identify themselves to their recipients with these IDs in order to be associated with the proper connection. Applications on both sides obtain references to their connections in order to send or receive data through them.

Client NetWare stations keep track of connections with the "Connection Reference" table. All resources and data regarding a particular connection are associated with a single entry in this table (see Figure 3).

Figure 3: Applications use connection handles which refer to shared connection references.

The NetWare client software, running on a client, maximizes the efficient use of connections by using a "Connection Handle" table to help share connection references between applications.

For example, in Figure 3 applications 2 and 3 have been given two different handles to the same connection reference by the NetWare client. The number of application users is reflected by the connection reference's usage count.

The NetWare client multi-tasks all client application's requests to a connection reference without their knowledge.

As applications close connection handles, the usage count in the associated connection reference is decremented. When a connection reference's usage count is decremented to zero, the NetWare client frees the connection reference and its resources.

NetWare Name Resolution

Every network must have some means to establish connections, control file access, security, and so on. Otherwise, there would be utter chaos. NetWare uses NetWare Core Protocol (NCP) packets to perform control operations throughout the network. For example, when a NetWare 5 client station starts up, it must establish a connection to its primary NDS server.

Note: The NCP packet names in this lesson have been modified to be more descriptive.

A client's primary connection identifies the server to which its user originally logged in. Figure 4 shows three NCP packets that could be used to establish an initial connection with the client's primary NDS server. Assuming that the client is just starting up, the first NCP packet is intended to obtain a connection with any server on the network that can help resolve the name of the primary server, (find its network address). In this illustration, Server A is the server responding to the client's ping.

After the client has a connection with this helper server, the client sends a second NCP packet to the helper server to resolve the target server name (in this case Server C) into an internetwork address.

Every NetWare server maintains a Service Advertising Protocol or SAP table which lists server names and their addresses on the network. Server A responds to the NCP name resolution request by finding Server C's name in its SAP table and returning the associated internetwork address.

The client then puts Server C's address into a new connection reference and uses the received internetwork address to send packets to Server C, exchanging information with it to initialize a connection.

Figure 4: NetWare uses NCP packets to control the network.

Primary Authentication

Servers often contain confidential data (for example, employee payroll and home addresses). On a network where all stations have access to the same network cable, making sure that only the right people can read and understand transmitted confidential data from the wire is problematic.

As discussed in NDS101, NDS is a distributed database containing directory information. Distributed usually means that multiple servers participate in maintaining the directory. Conversely, the directory is often used to control access to the secure services and data residing on those NDS servers. The directory does this by requiring an accessing client application to prove that its user is in fact someone who has rights to access requested services or data.

NDS authentication is the process of proving one's identification to NDS. Authentication is accomplished by having an object in the NDS tree represent the user requesting access. This object contains or has access to all of the pertinent information about the user (i.e., password signature, encryption keys, etc.) that NDS needs to prove that a user communicating from a client machine is who he says he is.

When a user logs in to NDS through a client application (the Novell login utility can be considered such an application), the client application engages in a primary authentication protocol with NDS. If the user can provide the information which enables the application to successfully complete the primary authentication protocol (i.e., the password), the NetWare client and the NDS server will designate the connection between them to be authenticated. From that point on, the authenticated user will have access to every object which has the authenticated user object's distinguished name listed in its access control list or ACL.

Note: See the NDS101 lessons for explanations of tree, object, attribute, distinguished name, etc.

Every object in NDS has an access control list similar to the simplified ACL shown in Figure 5. The entries in an object's ACL are distinguished names of other objects selected by an administrator. The administrator marks each object's ACL entries for various levels of access (i.e., read, write, and so on) to the object and its attributes as shown in Figure 6.

Since administration objects often contain information required to gain access to services and data running on NDS servers, NDS becomes a finely grained access control mechanism for all of the data and services that are represented in its tree.

Figure 5: A simplified ACL.

Figure 6: Administrators manage access rights to objects.

Connection States

The NetWare connection model has three states:

  • Unauthenticated

  • Authenticated but not licensed

  • Licensed

Unauthenticated

A connection in the first state is not yet authenticated. This means that the corresponding stations have established unique connection IDs for each other, but the necessary authentication protocol has not been completed. A client connection in this state is denied access to all essential NetWare services, such as the NetWare file system.

Authenticated but Not Licensed

Each NetWare network has a limited number of licensed connections. This number is set at the time of purchase. A connection in the second state is authenticated but not licensed. This means that the appropriate credentials have been created for the authenticated connection but NetWare has not awarded it with a licensed status. Without a licensed connection, the client still cannot use essential NetWare services.

Licensed

An authenticated and licensed connection allows the client full use of all services and data that the client's user object has been given rights to in the tree. A connection becomes licensed by using an essential NetWare service. If a license is available, the connection becomes licensed. If not, the use of the service is denied. The connection remains unlicensed until a license frees up and an essential service is requested again through the connection.

Obtaining an Authenticated Client-side Connection

After an application (the login utility, for example) establishes an authenticated connection to a server, users should not be required to re-enter their name and password every time a different application needs to connect to the same server.

To prevent this from happening when an application requests a connection handle, the NetWare client will check the connection reference table to see if a connection already exists to the specified server or tree. If one does exist, the NetWare client will create another handle to the connection reference and increment the usage count for the associated connection reference.

The application should test the connection handle to make sure that the connection it references is authenticated. If it is, the handle can be used as is. If not, the application must obtain a user name and password from the user to perform the authentication.

If an existing authenticated connection can be used, the application can obtain the new connection handle without ever asking the user for a password.

Background Authentication

After any client station application establishes an authenticated connection to a server on a given tree, the client's user should not be required to re-enter her name and password every time a connection to a different server on the same tree is needed.

Background authentication is the process NDS uses to obtain subsequent authenticated connections to different servers on the same tree.

After a primary authenticated connection between a client application and an NDS server has already been established, the other NDS servers in the tree do not know that the client has been authenticated. So, a connection request coming from the client to a different server on the same tree must be reauthenticated.

If the client indicates that it already has an authenticated connection to the server's tree, the receiving NDS server will attempt to verify that assertion. The NDS server and NetWare client perform this verification by following a secondary authentication protocol (Refer to Figure 7).

If the client has already established an authenticated connection to the tree, it will have some secret material (credentials) obtained as a side-effect of the primary authentication protocol. The secondary server will interact with the client and NDS to determine if the client possesses the credentials. If it does, the new connection is authenticated without the user having to re-enter a password. If it doesn't, the user will need to provide a password and name to log in. The details of how this is done are explained later in this article.

Figure 7: A user can be connected to several different servers on the same tree.

Security Terms

This section defines some important security terms. These terms are used later to describe how NetWare authentication is accomplished.

Symmetric Key Cryptography

A message exchange using a symmetric key requires the use of the same key by all parties involved for both encryption and decryption. This necessitates distributing the key to all participants.

When using symmetric encryption/decryption all parties must trust each other, because they can read each other's messages.

Physical distribution of symmetric keys is the safest because an unencrypted electronic transmission of a key would be vulnerable to theft. Physical key distribution would make symmetric keys hard to use in an environment with a large number of users. However, NetWare uses a clever combination of encryption schemes to distribute symmetric keys over the network for background authentication. This is described later in the protocol explanations.

Asymmetric Key Cryptography

In asymmetric encryption/decryption, each participant possesses a private and a public key. Messages encrypted with the private key can only be decrypted with the corresponding public key. Messages encrypted with the public key can only be decrypted with the corresponding private key.

Participants keep their private keys and distribute their public keys to other participants. When one participant wants to send a secure message to another, the message is encrypted with the other persons public key and sent. This is safe because the only person that can decrypt the message is the one who has the private key. The other person can then send a secure message back to the first person by using the first person's public key. Only the person with the private key can decrypt the message and that will be the first person.

Asymmetric key encryption/decryption is particularly suited to network communication because of the ease in which keys can be securely distributed.

Secure Hashes (Message Digests/Signatures)

A secure hash algorithm permeates a source message of variable length into a highly unique, fixed-length digital fingerprint (signature). Secure hashes cannot be used to reconstitute the original message but can be used by one participant to prove that another participant possesses some secret material (for example, a password). This proof can then be used to establish an identity.

NetWare's Primary Authentication Protocol

The goal of the primary NDS authentication protocol is to prove to an NDS server in the target tree, that the user has entered the correct password. The actual password is never passed over the wire in any form. Refer to Figure 8 for the following description of NetWare's primary NDS authentication protocol.

Figure 8: NDS's primary authentication protocol.

Note: The variable names used in these descriptions have been invented for ease of description and have no relationship to standard Novell nomenclature.

  1. The user enters his or her name and password into a login dialog running on the workstation. The workstation sends the user's distinguished name encapsulated in an authentication request to the designated primary NDS server in the NDS tree.

  2. The NDS server connected to the client uses the received distinguished name to obtain a reference to the user's object which contains the user's public and private keys and password hash.

    ndsChallenge is a random number that NDS has already generated to use in authentication transactions. The NDS server connected to the client generates another random number especially for this authentication session that we will call serverChallenge. The server then sends serverChallenge and ndsChallenge along with NDS's own public key to the client workstation.

  3. The workstation hashes the password together with the received ndsChallenge to obtain a value,clientX. The workstation generates another random number we will call clientChallenge and hashes clientX with it to obtain clientY.

    The workstation performs asymmetric key encryption on clientY and clientChallenge using the NDS public key and sends the message to the server.

    The server performs asymmetric key decryption of the received message using the NDS private key to obtain clientY and clientChallenge.

    The server hashes the password hash (obtained from the user's object) with ndsChallenge to obtain a value we will call serverX.

    The server hashes serverX with clientChallenge to obtain serverY.

    The server compares serverY with received clientY. If they are the same, then NDS determines that the client must have the correct password.

  4. Using the user's encrypted private key (obtained from the user's object) and an authentication period timeout value, the server uses a variant of the Gillou-Quisquater algorithm to generate a key we will call shortTimeKey. The unique thing about this key is that it is equivalent to the user's private key but only for a limited length of time.

    The server symmetric key encrypts the shortTimeKey using clientY as the secret key and sends the encrypted message to the workstation. Then it promptly throws the client's shortTimeKey and other authentication materials away.

  5. The workstation performs symmetric key decryption on the received message using clientY as the secret key to obtain shortTimeKey.

    The workstation is now authenticated to the NDS tree. It will use the shortTimeKey to background authenticate with other NDS servers on the tree until the shortTimeKey expires (the authenticated period timeout occurs).

NetWare Background Authentication Protocol

Once a client workstation has what we have referred to as a shortTimeKey, the client will be able to background authenticate to any other NDS server on the tree using the protocol described below. Refer to Figure 9 for the following discussion.

Figure 9: Server-side background authentication protocol.

The following steps are completed during server-side background authentication:

  1. The NetWare client will pass the user's distinguished name to the new server when any application on its workstation attempts to get a connection to a secondary NDS server participating on a tree to which the client has already performed a primary authentication. The client will also send the server an asymmetric key encrypted handshake message using the shortTimeKey it obtained from the primary authentication process as the private key.

  2. The NDS server uses the distinguished name from the client to obtain a reference to the user's object (which it can use to obtain the user's security information). The server then attempts to asymmetric key decrypt the client's handshake message using the user's public key. If the handshake message decrypts to a usable handshake, then the server determines that the client must possess a valid shortTimeKey and so must already be authenticated to the tree. If the client is authenticated, the NDS server authenticates the connection on its side and generates a sessionKey for symmetrical encryption and decryption on both sides. It then encrypts the sessionKey with the user's public key and sends it to the client.

  3. The workstation and NDS server will sign all messages sent to each other with the sessionKey to ensure the integrity of transmitted data.

Source Code

Below is a source code listing for NDS102 using NDAP. All NDAP data types and functions are in bold. The large italic, double-underlined numbers refer to explanations after the code listing.

Novell provides C libraries for all kinds of purposes. This example uses the netwin32, clxwin32, locwin32, and calwin32 libraries to access the Client32 DLLs installed on a NetWare client machine. Among other things, the Client32 DLLs provide NDS tree access functionality. You can install Novell's Libraries for C from the Web:http://developer.novell.com/ndk/

Once you have installed the NetWare SDK, make sure that you add the appropriate path names to your development environment so that it can access the Netware headers and the libraries before attempting to build this sample application.

/**************************************************************************

  NOTE:

      This code requires the following NDAP client32 libraries:

           netwin32.lib, clxwind32.lib, locwin32.lib, calwin32.lib  

  **************************************************************************\



  #include <nwlocale.h><
  #include <nwdsdefs.h><
  #include <nwcalls.h><
  #include <ctype.h><
  #include <nwnet.h><
  #include <nwclxcon.h><


  // routines in this file to establish authenticated connection

  void __declspec(dllexport) WINAPI  getValOverAuthConn( LPSTR theBuffer );

  NWDSContextHandle                  defineContext( char *nameContext );

  NWCONN_HANDLE N_FAR                getConnHdlForTree();



  // globals

  NWDSContextHandle context = -1;





  // stuff from NDS101 using NDAP example

  extern  LPSTR treeName;    // target tree name - installed into the context

  extern  LPSTR objectName;    // our fully distinguished user name

  extern void __declspec(dllexport)WINAPI getFieldStringValue(LPSTR theBuffer);





  // routines from utils.c  - Utility routines that contain no NetWare calls

  extern  void  filterDName        ( char * sourceDistName );

  extern  void  deleteStr          ( char * targetStr , char * findStr );

  extern  void  getNameAndContext  ( char * sourceDistName ,

                                  char * nameBuffer ,

                                  char * contextBuffer );

  extern  void  filterNWName       ( char * nameStr ,char   delimiter );

  extern  long  displayError       ( char * SysRoutineName ,long   errVal );





  /****************************************************************************/

  /* Function : getValOverAuthConn ( )                                        */

  /* Description :                                                            */

  /*   Function for making authenticated connection to DS tree and then       */

  /*    reading object data over connection                                   */

  /****************************************************************************/

  void __declspec(dllexport) WINAPI  getValOverAuthConn(LPSTR theBuffer)

  { NWCCODE        cCode;

    LCONV          convert;

    NWCONN_HANDLE  connHdl;

    char           userName[ MAX_DN_CHARS ];

    char           userContext[ MAX_DN_CHARS ];

    BOOL           doBackgdAuthenticate = FALSE;

    LPSTR          passWord   = "Larry";



1 /*Called by Window's startup code if client machine already has a

     connection to the network.  Called again here to confirm it worked */

     cCode = NWCallsInit(NULL, NULL);



  /*Get the locale information and initialize Unicode tables for NDS use */

    if( !cCode )

    { NWLsetlocale(LC_ALL, "");

      NWLlocaleconv((LCONV NWFAR *)&convert);&
      cCode = NWInitUnicodeTables(convert.country_id, convert.code_page);

    } 



    if (cCode)

    { displayError( "There is a problem with the NetWare client.", cCode);

  return;

 }



  /*parseDistinguishedName is a trivial routine to parse up the dist name in

    objectName into user name and context name.  No NetWare calls made here */

    getNameAndContext( objectName , userName , userContext );



2 /*defineContext builds the context for the user object used in the login */

    context = defineContext(userContext );

    if( context == -1 )

      return;

  

3 /*getConnHdlForTree attempts to find an existing connection to the target NDS

    tree specified by "treeName" */

    connHdl = getConnHdlForTree();

    if(connHdl != -1 )



4 {// if the client already has a connection on the tree

      doBackgdAuthenticate =   NWDSAuthenticate ( connHdl, 0, NULL);

      if( doBackgdAuthenticate) 

      { cCode = NWDSAuthenticateConn(context, connHdl);

        if(cCode)

        { NWCCCloseConn(connHdl);

          connHdl = -1;

          doBackgdAuthenticate = false;

        }

      }

5 if( !doBackgdAuthenticate) 

  { cCode = NWDSLogin (context, 0L, userName, passWord, 420L);

    if (cCode)

    { switch(cCode)

      { case (NWCCODE)ERR_FAILED_AUTHENTICATION:

        { displayError( "Make sure you type in the correct password.", cCode);

          break;

        }

        case 0x889A:

        { displayError( "User object not found in specified context.", cCode);

          break;

        }

        default:

        { displayError( "NWDSLogin", cCode);

          break;

          }

        }

    /*  NWDSLogin() gets a connection, but doesn't return a connHdl,so we get an explicit

        reference to one here, if we need it, after NWDSLogin establishes a connection */

        if( connHdl == -1 )

          connHdl = getConnHdlForTree();

        }

      }

  

6   getFieldStringValue( theBuffer);

  }

 

3 /****************************************************************************/

  /* Function : getConnHdlForTree  ( )                                        */

  /* Description :                                                            */

  /*   Search through the connRef table to obtain a connection to a server in */

  /*    the tree specified by treeName                                        */

  /****************************************************************************/

  NWCONN_HANDLE N_FAR getConnHdlForTree()

  {

  NWCCODE       cCode;

  nuint32       scanIteration = 0 ;

  nuint32       connRef = -1;

  NWCONN_HANDLE connHdl = -1;

  char          preferredTreeName[NW_MAX_TREE_NAME_LEN];



    while( NWCCScanConnRefs( &scanIteration, &connRef )!= NWE_NO_MORE_ENTRIES)&
    { cCode = NWCCGetConnRefInfo (connRef ,  NWCC_INFO_TREE_NAME,

                            NW_MAX_TREE_NAME_LEN , preferredTreeName);

    if(cCode)

    { displayError( "NWCCScanConnRefs", cCode);

      break;

    }

    else

    { filterNWName( preferredTreeName , '_' );

      if(!_stricmp(preferredTreeName,treeName))

      { cCode = NWCCOpenConnByRef ( connRef, NWCC_OPEN_LICENSED,

                              NWCC_RESERVED , &connHdl);&
        if(cCode!= SUCCESS)

          displayError( "NWCCOpenConnByRef", cCode);

        break;

       }

     }

   } 

   return(connHdl);

  }

2 /****************************************************************************/

  /* Function : defineContext  ( )                                            */

  /* Description :                                                            */

  /*   Set up context just the way we want it                                 */

  /****************************************************************************/

  NWDSContextHandle defineContext( char *nameContext )

  {

  NWCCODE           cCode;

  NWDSContextHandle context;

  uint32            flags;



  //create a new context

    cCode = NWDSCreateContextHandle(&context );&
    if (cCode)

    { displayError( "NWDSCreateContextHandle", cCode );

      return (-1);

    }



  //set the context to the user's distinguished name

    cCode = NWDSSetContext(context, DCK_NAME_CONTEXT, nameContext );

    if (cCode)

    { NWDSFreeContext (context);

      displayError( "NWDSSetContext1", cCode );

      return (-1);

    }

  //set the default flag state of the context

    cCode = NWDSGetContext(context, DCK_FLAGS, (void *)&flags);&
    if ( cCode )

    { NWDSFreeContext(context);

      displayError( "NWDSGetContext" , cCode );

      return (-1);

    }

  //make sure the flag state of the context has what we want too!

    flags |= DCV_TYPELESS_NAMES;

    flags |= DCV_XLATE_STRINGS;

    flags |= DCV_DEREF_ALIASES;

    flags |= DCV_DEREF_BASE_CLASS;

    cCode   = NWDSSetContext (context, DCK_FLAGS, (void *)&flags );&
    if ( cCode )

    { NWDSFreeContext(context);

      displayError( "NWDSSetContext-Flags",cCode );

      return (-1);

    }



  //set the context to point to the target tree

    cCode = NWDSSetContext( context, DCK_TREE_NAME, treeName );

    if(cCode!=N_SUCCESS)

    {  NWDSFreeContext (context);

       displayError( "NWDSSetContext", cCode );

       return (-1);

    }

    return ( context );

  }
  1. We begin with the first call in the basic NetWare client initialization code.

    Before using any of the client API functions, NWCallsInit must be called to initialize double-byte tables and low-level interface functions. In most cases this will already be done for you by the NetWare client when the user initially logs in.

    NetWare uses data stored in Unicode tables to enable applications for a particular country's character set and forms of notation. When data is sent to NDS, Unicode tables are used to translate characters from your character set to Unicode format. When data is received from NDS, the Unicode tables are used again to translate from Unicode format to your local character set. NWLsetLocale and NWLlocaleconv determine which tables to use for a given location.

    The NWInitUnicodeTables call is used to load tables for a specific code page and country identifier.

  2. We must create and initialize the correct NDS context (NDS context refers to a location in the NDS Directory tree.)

    An NDS context is like a data structure; however, a programmer cannot directly access its members. Notice in the defineContext routine, that there are more members than just a name context. After the context has been fully initialized, it will be sent to the server as a parameter in the NWDSLogin and other NDS calls. The server will use this context information to locate the client's object in the tree so that the client can be authenticated to the network.

    Obtaining and setting up an NDS context was described in the NDS101 NDAP example. Because the NDS context is so important to programming with NDAP, we include the source code here just as a reminder.

  3. Many NetWare calls require a connection handle as a parameter. A call such as the one below will easily obtain a connection handle for you to the nearest server participating on a particular tree.

/*  N_EXTERN_LIBRARY NWRCODE NWCCOpenConnByName (NWCONN_HANDLE   startConnHandle,

                                                   pnstr8           name,

                                                   nuint            nameFormat,

                                                   nuint            openState,

                                                   nuint            tranType,

                                                   pNWCONN_HANDLE   pConnHandle);*/





  NWCCOpenConnByName ( 0 , treeName,NWCC_NAME_FORMAT_NDS_TREE,NWCC_OPEN_LICENSED,

                                    NWCC_TRAN_TYPE_WILD,&connHdl);&

NWCCOpenConnByName is the way to get a connection handle if you have a name for the target server or tree. There are similar calls if you are starting with something else, (a network address for example).

For all of these calls, the NetWare client will attempt to obtain a pre-existing connection to the target by looking through the connRef table. And if a connection doesn't already exist, the client will attempt to obtain a new connection.

This example code could use NWCCOpenConnByName to obtain its connection handle, but instead uses its own routine, getConnHdlForTree to search through the connRef table. This way you can see how the task can be done.

As mentioned earlier, the client's Conref table contains references to each of the client's current connections and the data associated with each connection, including the connected station's NDS tree name.

In this example's getConnHdlForTree routine, the NWCCScanConnRefs call sequences through the client's connRef table obtaining references for each entry in the table.

Each connRef structure obtained by NWCCScanConnRefs will contain the data shown in Figure 10. The connRef data is protected, but NetWare gives us access to it via the NWCCGetConnRefInfo call.

Figure 10: Connection reference information available to applications.

This application is looking for a connection to a specific tree. So, the NWCCGetConnRefInfo call is passed an infoType key of NWCC_INFO_TREE_NAME to obtain the name of the tree that the server on each connection is participating in. It doesn't really matter which server is selected as long as that server is participating in the correct tree.

NetWare tends to pad its names with underscores. After the underscores have been removed, if the tree name compares with the tree that we are looking for, then a connRef describing a connection to the desired tree has been found.

Note: We are using _stricmp( ) for the name compare because case doesn't matter.

If getConnHdlForTree finds a connRef to the desired tree, it initializes a new connection handle from it with the NWCCOpenConnectionByRef call. A successful call to NWCCOpenConnectionByRef ensures that the application has a connection handle that is at least at the first connection state, connected but not authenticated.

The NetWare connection model has three connection states. The first, connected but not authenticated, means that your station has an internetwork address for the station that it wants to communicate with but does not have the credentials necessary to use any essential services, such as the NetWare file system (see the connection table description in the concepts section above).

  1. If the code was able to find a connection to the desired tree, it checks with the NWDSAuthenticate call to see if the found connection is already in the second connection state, connected and authenticated.

    If the connection isn't authenticated, the boolean doBackgdAuthenticate variable will be non-zero (in this case, 0 means NWDSAuthenticate was able to authenticate the connection) and the code will attempt to perform a background authentication with the NWDSAuthenticateConn call.

    If there is another connection to the tree in the connRef table that has already successfully performed the primary authentication protocol, then the client will possess the necessary credentials and NWDSAuthenticateConn will successfully perform the background authentication protocol. If the attempt at background authentication fails, the code deletes the connection handle.

  2. All of the above is not necessary if all you want to do is make NDS calls like NWDSRead. NWDSLogin does it all automatically.

    NWDSLogin scans the connRef table for the tree specified in its context parameter and if NWDSLogin finds a connection to the tree, it makes sure that it is authenticated. If it isn't NWDSLogin performs the primary authentication protocol with the NDS server at the other end to authenticate it (one of its parameters is a password). Once an authenticated connection is obtained, the application can perform NDS calls as long as the application is running.

    If NWDSLogin can't find an existing authenticated connection, it makes one. The problem is, it doesn't give us a handle to the connection. That's why we had to do so much work in steps 3 and 4.

    The NDS calls in getFieldStringValue() from NDS101 may not require a connection handle, but there are plenty of NetWare calls that do. I just wanted to give you an example of how to get one. We will have examples of NW calls which require an explicitly referenced connection handle in later DeveloperNet University lessons.

    An authenticated credential puts the client into the second state in the connection model. Even though the connection has been authenticated, the network has not licensed it yet, because an essential service (such as a file operation) has not been requested.

    If the NWDSLogin call was necessary to obtain the authenticated connection, then connHdl will be a -1. In that case getConnHdlForTree will get an explicit connection handle reference for the connection established by NWDSLogin.

  3. The getFieldStringValue call was described in "NDS101 Using NDAP". It reads the attribute specified by attributeName, from the object specified by objectName, on the tree specified by treeName, over an already existing connection. The already existing connection was established by the code previous to this call.

Note: Since the NDS calls in getFieldStringValue access essential NetWare services to read the target attribute, the connection will move to the third connection state (connected, authenticated, and licensed), if it wasn't already.

Conclusion

NDS programming isn't difficult. During this article, we have discussed NetWare connections, NetWare name resolution, primary authentication, connection states, obtaining an authenticated client-side connection, background authentication, security terms, NetWare's primary authentication protocol, and NetWare's background authentication protocol. We have also learned how to write a C application that uses NDAP to establish an authenticated connection to an NDS tree.

As you experiment with the code example, try rebuilding it to connect to different trees (if available).

Other NDS101 examples will present additional ways to develop to NDS, like ActiveX.

For more information about other examples in this series, refer to: http://developer.novell.com/education/

To download the project corresponding to this article, refer to: http://www.novell.com/coolsolutions/tools/15624.html


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.

© 2014 Novell