Novell is now a part of Micro Focus

Novell Directory Services Q & A

Articles and Tips: article

JOHN BUCKLE
Developer Support Engineer
Developer Support

01 Dec 1997


Q.Why do NWDSRead(), NWDSReadObjectInfo() and similar functions return ERR_NO_SUCH_ENTRY (-601) when given the name of an existing NDS object?

A. The reason why ERR_NO_SUCH_ENTRY is returned is normally caused by having an incorrect name context or by the default typing rules getting it wrong.

Unless the object name given to NWDSRead() etc. is preceded by a period the name context held in the context parameter is appended to the object name to make the full distinguished name. If it happens that the object name given to the function is the distinguished name already, and the name context in context parameter is not a null string, then the distinguished name used by the function will be the concatenation of the object name and name context, which is guaranteed not to be what you want.

To resolve this either insert a period before the object name, turn off the automatic canonical name flag or set the name context to [ROOT], the name context null string. The latter is normally the easier option and can be done using the command below.

NWDSSetContext(handle,DCK_NAME_CONTEXT,DS_ROOT_NAME) ;

The default naming rules can also play havoc with un-typed distinguished names. The default naming rules insert a CN= in front of the left most component, OU= in front of the middle components and O=in front of the right most component (or C= if there is a country object in the tree).

So the name

jbuckle.support.sydney.developenet

becomes

cn=jbuckle.ou=support.ou=sydney.o=developernet

This works in most cases since the object name being qualified is a leaf object whose left most component is a common name. However if the object name is not a common name then the default typing rules will generate an incorrect typed distinguished name. This incorrect name is then passed onto directory services, which will not be able to resolve the name and return the error. To see this happening use LANAnalyzer and use NWDSRead() to read a container object, for example support.sydney.developernet.

You will be able to see packet traces trying to resolve the name

cn=support.ou=sydney.o=developernet.

There are a few ways to help the typing rules to get it right. If you know the type of the first component and it is not CN then insert the type name before using it. The default naming rules are only used on un-typed components. If you don't know the type then try OU= if the first attempt fails, this is demonstrated in Listing 1. Lastly, if the object is a container then set the name context to the container and pass a null string for the object name, as shown below.

NWDSSetContext(handle.DCK_NAME_CONTEXT,objectName)

;

        NWDSRead(handle,"", ... ) ;

Listing 1

bool GetDistName(char * object, char * distName)

{

Object_Info_T info ;



    if ((ccode = NWDSReadObjectInfo(Context,object,distName,& info)) == 0)

        return true ;



    // Try again setting the type of the first component to OU in case

    // it is a container. Default naming rules would type this as CN.

    sprintf(distName,"OU=%s",object) ;



    ccode = NWDSReadObjectInfo(Context,distName,distName,& info) ;& info) ;


    return ccode == 0 ;

}

Q. Is it possible for objects not of class User to authenticate to an NDS tree using NWDSLogin()? Can you use LOGIN.EXE to login at a workstation?

A. Yes to both questions. For an object to authenticate to an NDS tree the object needs to have the public and private key attributes used by the authentication system. In the default schema the public and private key attributes are only used by the User and Server classes; however, the attributes can be added as optional attributes to existing classes or to new classes.

For example, Listing 2 defines a new class called AuthenticatedGroup that have the key attributes as optional attributes to the Group class. Listing 3 shows how to create a object of this new class.

The private key attribute cannot be added as a mandatory attribute to a class since it has the DS_HIDDEN_ATTR property, so it is not possible to assign this attribute a value when the object is created. Since the private key cannot be a mandatory attribute there is little point defining the public key attribute as mandatory. The private key value is set in conjunction with the public by calling NWDSGenerateObjectKeyPair().

Listing 2

char * AuthGroupClassNameDef[] = {

   "AuthenticatedGroup",   // Class name

   "GROUP", 0,             // Base class

   0,                      // Containment classes - use base class

   0,                      // Naming attributes - use base class

   0,                      // Mandatory attributes - use base class

   "Public Key",           // Optional attributes

   "Private Key", 0,

   0} ;



bool createClassDef(char ** classDef)

{

Class_Info_T    classInfo ;



    memset(& classInfo,0,sizeof(classInfo)) ;& classInfo,0,sizeof(classInfo)) ;


    // Set the classFlags classInfo to define a DS_EFFECTIVE_CLASS.

    classInfo.classFlags = DS_EFFECTIVE_CLASS ;



    // Initialise InputBuffer using NWDSInitBuf() with DSV_DEFINE_CLASS

    NWDSInitBuf(Context,DSV_DEFINE_CLASS,Input) ;



    char * className = * classDef++ ;



    for (int i = 0 ; i < 5 ; i++){

        NWDSBeginClassItem(Context,Input) ;

        while (* classDef)

            NWDSPutClassItem(Context,Input,* classDef++) ;

        classDef++ ;

    }

    ccode = NWDSDefineClass(Context,className,& classInfo,Input) ;& classInfo,Input) ;


    return ccode == 0 || ccode == ERR_CLASS_ALREADY_EXISTS ;

}

Listing 3

bool createAuthGroupObject(char * objectName, char * password)

{

    // Initialise Input buffer

    NWDSInitBuf(Context,DSV_ADD_ENTRY,Input) ;



    // Add the mandatory attributes to Input buffer.

    NWDSPutAttrName(Context,Input,"Object Class") ;

    NWDSPutAttrVal (Context,Input,SYN_CLASS_NAME,"AuthenticatedGroup") ;



    // Create a new NDS object using NWDSAddObject().

    if ((ccode = NWDSAddObject(Context,objectName,0,0,Input)) != 0)

        return false ;



    // Generate object key pair and set the password

    ccode = NWDSGenerateObjectKeyPair(Context,objectName,password,0) ;

    return ccode == 0 ;

}

Q. Why is it I cannot open NDS Stream values on a Windows NT workstation?

A. The documentation for NWDSOpenStream() states, "The returned handle is a file handle that is appropriate for the platform on which the application is running. This file handle can be used to access the attribute value through the platform's standard file I/O functions." In the case of Win32 this is an operating-system file handle.

The function _open_osfhandle() has to be used to associate a C run-time file handle with the existing operating-system file handle. The C run-time file handle can then be used with standard functions like read(), write() etc. This is demonstrated in Listing 4.

Listing 4

NWDSCCODE ReadLoginScript(NWDSContextHandle handle, char * user)

{

NWFILE_HANDLE nwHandle = 0 ;

NWDSCCODE ccode ;



    ccode = NWDSOpenStream(handle,user,"Login Script",1L,& nwHandle) ;& nwHandle) ;
    if (ccode) return ccode ;



    int fh = _open_osfhandle((long)nwHandle,_O_RDONLY) ;

    if (fh == -1) return -1 ;



    char buffer[256] ;

    while (read(fh,buffer,sizeof(buffer)) > 0)

        printf(buffer) ;

    printf("") ;



    close(fh) ;

    return 0 ;

}

Q. Is it possible to open a Bindery connection to a server in an NDS tree, where you have already authenticated to the tree?

A. Yes you can, but you could face complications if you already have an NDS connection to that server and the connection is being used to monitor your NDS connection. Also, why would you want to open a Bindery connection when you could use NWDSAuthenticateConn() and establish an NDS connection?

Q. Because I need a connection to the server under a different user name. Anyway, I ask the questions.

A. If you already have a connection to the server then you have to close the connection before using NWLoginToFileServer() to establish the Bindery connection. To close a connection you can use

NWFreeConnectionSlot(connHandle,SYSTEM_DISCONNECT)

;

If the connection is a monitored connection then Client 32 will attempt to find a new server with a writeable replica so that the login attributes can be monitored. If a new server cannot be found then an error is returned.

Listing 5 demonstrates how to establish a Bindery connection to a server.

Listing 5

NWCONN_HANDLE LoginToAdminServer(char * server,

               char * userName, char * password)

{

NWCONN_HANDLE conn ;

NWRCODE       ccode ;

nuint         state = 0 ;



    // Get connection handle to server

    ccode = NWCCOpenConnByName(0,server,

                NWCC_NAME_FORMAT_BIND,

                NWCC_OPEN_UNLICENSED,

                NWCC_TRAN_TYPE_IPX,& conn) ;& conn) ;
    if (ccode)

        return Error("Cannot open connection to server\n") ;



    NWCCGetConnInfo(conn,NWCC_INFO_AUTHENT_STATE,sizeof(state),& state) ;& state) ;


    if (state){

        if (NWFreeConnectionSlot(conn,SYSTEM_DISCONNECT))

            return Error("Cannot clear connection to server\n") ;

        ccode = NWCCOpenConnByName(0,server,

                    NWCC_NAME_FORMAT_BIND,

                    NWCC_OPEN_UNLICENSED,

                    NWCC_TRAN_TYPE_IPX,& conn) ;& conn) ;
        if (ccode)

            return Error("Cannot reconnect to server\n") ;

    }



    // Authenticate using user name and password

    ccode = NWLoginToFileServer(conn,username,OT_USER,password) ;

    if (ccode){

        Error("Cannot attach to server\n") ;

        return NWCCCloseConn(conn), 0 ;

    }



    // License connection so that files can be accessed

    ccode = NWCCLicenseConn(conn) ;

    if (ccode){

        Error("Cannot license connection to server\n") ;

        return NWCCCloseConn(conn), 0 ;

    }

    return conn ;

}

* 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.

© Copyright Micro Focus or one of its affiliates