// This file is based heavily on nslcd.h from nss-pam-ldapd // Copyright (C) 2015, 2017 Luke Shumaker /* nslcd.h - file describing client/server protocol Copyright (C) 2006 West Consulting Copyright (C) 2006, 2007, 2009, 2010, 2011, 2012, 2013 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package nslcd_proto import ( "fmt" "io" "net" ) /* The protocol used between the nslcd client and server is a simple binary protocol. It is request/response based where the client initiates a connection, does a single request and closes the connection again. Any mangled or not understood messages will be silently ignored by the server. A request looks like: INT32 NSLCD_VERSION INT32 NSLCD_ACTION_* [request parameters if any] A response looks like: INT32 NSLCD_VERSION INT32 NSLCD_ACTION_* (the original request type) [result(s)] INT32 NSLCD_RESULT_END A single result entry looks like: NSLCD_RESULT_BEGIN int32 [result value(s)] If a response would return multiple values (e.g. for NSLCD_ACTION_*_ALL functions) each return value will be preceded by a NSLCD_RESULT_BEGIN value. After the last returned result the server sends NSLCD_RESULT_END. If some error occurs (e.g. LDAP server unavailable, error in the request, etc) the server terminates the connection to signal an error condition (breaking the protocol). These are the available basic data types: INT32 - 32-bit integer value TYPE - a typed field that is transferred using sizeof() STRING - a string length (32bit) followed by the string value (not null-terminted) the string itself is assumed to be UTF-8 STRINGLIST - a 32-bit number noting the number of strings followed by the strings one at a time Furthermore the ADDRESS compound data type is defined as: INT32 type of address: e.g. AF_INET or AF_INET6 INT32 lenght of address RAW the address itself With the ADDRESSLIST using the same construct as with STRINGLIST. The protocol uses network byte order for all types. */ // These basic data types are implemented in `io.go` /* The current version of the protocol. This protocol should only be updated with major backwards-incompatible changes. */ const NSLCD_VERSION int32 = 0x00000002 /* Get a NSLCD configuration option. There is one request parameter: */ type Request_Config_Get struct { Key int32 /* NSLCD_CONFIG_* */ } /* the result value is: */ type Config struct { Value string /* interpretation depending on request */ } const NSLCD_ACTION_CONFIG_GET int32 = 0x00010001 const ( /* return the message, if any, that is presented to the user when password modification through PAM is prohibited */ NSLCD_CONFIG_PAM_PASSWORD_PROHIBIT_MESSAGE int32 = 1 ) /* Email alias (/etc/aliases) NSS requests. The result values for a single entry are: */ type Alias struct { Name string Recipients []string } const NSLCD_ACTION_ALIAS_BYNAME int32 = 0x00020001; type Request_Alias_ByName struct { Name string } const NSLCD_ACTION_ALIAS_ALL int32 = 0x00020008; type Request_Alias_All struct {} /* Ethernet address/name mapping NSS requests. The result values for a single entry are: */ type Ether struct { Name string Address [6]byte } const NSLCD_ACTION_ETHER_BYNAME int32 = 0x00030001; type Request_Ether_ByName struct { Name string } const NSLCD_ACTION_ETHER_BYETHER int32 = 0x00030002; type Request_Ether_ByEther struct { Ether [6]byte } const NSLCD_ACTION_ETHER_ALL int32 = 0x00030008; type Request_Ether_All struct {} /* Group and group membership related NSS requests. The result values for a single entry are: */ type Group struct { Name string PwHash string ID int32 Members []string } /* (note that the BYMEMER call returns an emtpy members list) */ const NSLCD_ACTION_GROUP_BYNAME int32 = 0x00040001; type Request_Group_ByName struct { Name string } const NSLCD_ACTION_GROUP_BYGID int32 = 0x00040002; type Request_Group_ByGid struct { Gid int32 } const NSLCD_ACTION_GROUP_BYMEMBER int32 = 0x00040006; type Request_Group_ByMember struct { Member string } const NSLCD_ACTION_GROUP_ALL int32 = 0x00040008; type Request_Group_All struct {} /* Hostname (/etc/hosts) lookup NSS requests. The result values for an entry are: */ type Host struct { Name string Aliases []string Addresses []net.IP } const NSLCD_ACTION_HOST_BYNAME int32 = 0x00050001; type Request_Host_ByName struct { Name string } const NSLCD_ACTION_HOST_BYADDR int32 = 0x00050002; type Request_Host_ByAddr struct { Addr net.IP } const NSLCD_ACTION_HOST_ALL int32 = 0x00050008; type Request_Host_All struct {} /* Netgroup NSS result entries contain a number of parts. A result entry starts with: STRING netgroup name followed by zero or more references to other netgroups or netgroup triples. A reference to another netgroup looks like: INT32 NSLCD_NETGROUP_TYPE_NETGROUP STRING other netgroup name A a netgroup triple looks like: INT32 NSLCD_NETGROUP_TYPE_TRIPLE STRING host STRING user STRING domain A netgroup result entry is terminated by: INT32 NSLCD_NETGROUP_TYPE_END */ type Netgroup_Netgroup struct { Name string } type Netgroup_Triple struct { Host string User string Domain string } type Netgroup_PartList []interface{} func (data Netgroup_PartList) nslcdWrite(fd io.Writer) { for _, part := range data { var t int32 = -1 switch part.(type) { case Netgroup_Netgroup: t = NSLCD_NETGROUP_TYPE_NETGROUP case Netgroup_Triple: t = NSLCD_NETGROUP_TYPE_TRIPLE } if t < 0 { panic(fmt.Sprintf("unrecognized netgroup type: %#08x", t)) } write(fd, t) write(fd, part) } write(fd, NSLCD_NETGROUP_TYPE_END) } func (data *Netgroup_PartList) nslcdRead(fd io.Reader) { *data = make([]interface{}, 0) for { var t int32 var v interface{} read(fd, &t) switch t { case NSLCD_NETGROUP_TYPE_NETGROUP: v = Netgroup_Netgroup{} case NSLCD_NETGROUP_TYPE_TRIPLE: v = Netgroup_Triple{} case NSLCD_NETGROUP_TYPE_END: return default: npanic(NslcdError(fmt.Sprintf("unrecognized netgroup type: %#08x", t))) } read(fd, &v) *data = append(*data, v) } } var _ nslcdObject = Netgroup_PartList{} var _ nslcdObjectPtr = &Netgroup_PartList{} type Netgroup struct { Name string Parts Netgroup_PartList } const NSLCD_ACTION_NETGROUP_BYNAME int32 = 0x00060001; type Request_Netgroup_ByName struct { Name string } const NSLCD_ACTION_NETGROUP_ALL int32 = 0x00060008; type Request_Netgroup_All struct {} const ( NSLCD_NETGROUP_TYPE_NETGROUP int32 = 1 NSLCD_NETGROUP_TYPE_TRIPLE int32 = 2 NSLCD_NETGROUP_TYPE_END int32 = 3 ) /* Network name (/etc/networks) NSS requests. Result values for a single entry are: */ type Network struct { Name string Aliases []string Addresses []net.IP } const NSLCD_ACTION_NETWORK_BYNAME int32 = 0x00070001; type Request_Network_ByName struct { Name string } const NSLCD_ACTION_NETWORK_BYADDR int32 = 0x00070002; type Request_Network_ByAddr struct { Addr net.IP } const NSLCD_ACTION_NETWORK_ALL int32 = 0x00070008; type Request_Network_All struct {} /* User account (/etc/passwd) NSS requests. Result values are: */ type Passwd struct { Name string PwHash string UID int32 GID int32 GECOS string HomeDir string Shell string } const NSLCD_ACTION_PASSWD_BYNAME int32 = 0x00080001; type Request_Passwd_ByName struct { Name string } const NSLCD_ACTION_PASSWD_BYUID int32 = 0x00080002; type Request_Passwd_ByUID struct { UID int32 } const NSLCD_ACTION_PASSWD_ALL int32 = 0x00080008; type Request_Passwd_All struct {} /* Protocol information requests. Result values are: */ type Protocol struct { Name string Aliases []string Number int32 } const NSLCD_ACTION_PROTOCOL_BYNAME int32 = 0x00090001; type Request_Protocol_ByName struct { Name string } const NSLCD_ACTION_PROTOCOL_BYNUMBER int32 = 0x00090002; type Request_Protocol_ByNumber struct { Number int32 } const NSLCD_ACTION_PROTOCOL_ALL int32 = 0x00090008; type Request_Protocol_All struct {} /* RPC information requests. Result values are: */ type RPC struct { Name string Aliases []string Number int32 } const NSLCD_ACTION_RPC_BYNAME int32 = 0x000a0001; type Request_RPC_ByName struct { Name string } const NSLCD_ACTION_RPC_BYNUMBER int32 = 0x000a0002; type Request_RPC_ByNumber struct { Number int32 } const NSLCD_ACTION_RPC_ALL int32 = 0x000a0008; type Request_RPC_All struct {} /* Service (/etc/services) information requests. The BYNAME and BYNUMBER requests contain an extra protocol string in the request which, if not blank, will filter the services by this protocol. Result values are: */ type Service struct { Name string Aliases []string PortNumber int32 Protocol string } const NSLCD_ACTION_SERVICE_BYNAME int32 = 0x000b0001; type Request_Service_ByName struct { Name string } const NSLCD_ACTION_SERVICE_BYNUMBER int32 = 0x000b0002; type Request_Service_ByNumber struct { Number int32 } const NSLCD_ACTION_SERVICE_ALL int32 = 0x000b0008; type Request_Service_All struct {} /* Extended user account (/etc/shadow) information requests. Result values for a single entry are: */ type Shadow struct { // It is my understanding that an empty value for an INT32 // field is expressed with a negative number. -- lukeshu Name string PwHash string LastChangeDate int32 MinDays int32 MaxDays int32 WarnDays int32 InactDays int32 ExpireDate int32 Flag int32 } const NSLCD_ACTION_SHADOW_BYNAME int32 = 0x000c0001; type Request_Shadow_ByName struct { Name string } const NSLCD_ACTION_SHADOW_ALL int32 = 0x000c0008; type Request_Shadow_All struct {} /* PAM-related requests. The request parameters for all these requests begin with: */ type PAM_Base struct { UserName string ServiceName string RUser string RHost string TTY string } /* If the user is not known in LDAP no result may be returned (immediately return NSLCD_RESULT_END instead of a PAM error code). */ /* PAM authentication check request. The extra request values are: */ type Request_PAM_Authentication struct { PAM_Base Password string } /* and the result value consists of: */ type PAM_Authentication struct { AuthenticationResult int32 UserName string AuthorizationResult int32 AuthorizationError string } /* If the username is empty in this request an attempt is made to authenticate as the administrator (set using rootpwmoddn). Some authorisation checks are already done during authentication so the response also includes authorisation information. */ const NSLCD_ACTION_PAM_AUTHENTICATION int32 = 0x000d0001 /* PAM authorisation check request. The result value consists of: */ type PAM_Authorization struct { Result int32 Error string } /* The authentication check may have already returned some authorisation information. The authorisation error message, if supplied, will be used by the PAM module instead of a message that is generated by the PAM module itself. */ const NSLCD_ACTION_PAM_AUTHORIZATION int32 = 0x000d0002; type Request_PAM_Authorization PAM_Base /* PAM session open request. The result value consists of: */ type PAM_SessionOpen struct { SessionID string } /* This session id may be used to close this session with. */ const NSLCD_ACTION_PAM_SESSIONOPEN int32 = 0x000d0003; type Request_PAM_SessionOpen PAM_Base /* PAM session close request. This request has the following extra request value: */ type Request_PAM_SessionClose struct { PAM_Base SessionID string } /* and this calls only returns an empty response value. */ type PAM_SessionClose struct {} const NSLCD_ACTION_PAM_SESSIONCLOSE int32 = 0x000d0004 /* PAM password modification request. This requests has the following extra request values: */ type Request_PAM_PwMod struct { PAM_Base AsRoot int32 /* 0=oldpasswd is user passwd, 1=oldpasswd is root passwd */ OldPassword string NewPassword string } /* and returns the result values: */ type PAM_PwMod struct { Result int32 Error string } const NSLCD_ACTION_PAM_PWMOD int32 = 0x000d0005 /* User information change request. This request allows one to change their full name and other information. The request parameters for this request are: STRING user name INT32 asroot: 0=passwd is user passwd, 1=passwd is root passwd STRING password followed by one or more of the below, terminated by NSLCD_USERMOD_END INT32 NSLCD_USERMOD_* STRING new value the response consists of one or more of the entries below, terminated by NSLCD_USERMOD_END: INT32 NSLCD_USERMOD_* STRING response (if the response is blank, the change went OK, otherwise the string contains an error message) */ type UserMod_Item struct { Key int32 Value string } type UserMod_ItemList []UserMod_Item func (data UserMod_ItemList) nslcdWrite(fd io.Writer) { for _, item := range data { write(fd, item) } write(fd, NSLCD_USERMOD_END) } func (data *UserMod_ItemList) nslcdRead(fd io.Reader) { *data = make([]UserMod_Item, 0) for { var t int32 read(fd, &t) if t == NSLCD_USERMOD_END { return } var v UserMod_Item read(fd, &v) *data = append(*data, v) } } var _ nslcdObject = UserMod_ItemList{} var _ nslcdObjectPtr = &UserMod_ItemList{} type Request_UserMod struct { UserName string AsRoot int32 Password string Items UserMod_ItemList } type UserMod struct { Items []UserMod_Item } const NSLCD_ACTION_USERMOD int32 = 0x000e0001 /* These are the possible values for the NSLCD_ACTION_USERMOD operation above. */ const ( NSLCD_USERMOD_END int32 = 0 /* end of change values */ NSLCD_USERMOD_RESULT int32 = 1 /* global result value */ NSLCD_USERMOD_FULLNAME int32 = 2 /* full name */ NSLCD_USERMOD_ROOMNUMBER int32 = 3 /* room number */ NSLCD_USERMOD_WORKPHONE int32 = 4 /* office phone number */ NSLCD_USERMOD_HOMEPHONE int32 = 5 /* home phone number */ NSLCD_USERMOD_OTHER int32 = 6 /* other info */ NSLCD_USERMOD_HOMEDIR int32 = 7 /* home directory */ NSLCD_USERMOD_SHELL int32 = 8 /* login shell */ ) /* Request result codes. */ const ( NSLCD_RESULT_BEGIN int32 = 1 NSLCD_RESULT_END int32 = 2 ) /* Partial list of PAM result codes. */ const ( NSLCD_PAM_SUCCESS int32 = 0 /* everything ok */ NSLCD_PAM_PERM_DENIED int32 = 6 /* Permission denied */ NSLCD_PAM_AUTH_ERR int32 = 7 /* Authc failure */ NSLCD_PAM_CRED_INSUFFICIENT int32 = 8 /* Cannot access authc data */ NSLCD_PAM_AUTHINFO_UNAVAIL int32 = 9 /* Cannot retrieve authc info */ NSLCD_PAM_USER_UNKNOWN int32 = 10 /* User not known */ NSLCD_PAM_MAXTRIES int32 = 11 /* Retry limit reached */ NSLCD_PAM_NEW_AUTHTOK_REQD int32 = 12 /* Password expired */ NSLCD_PAM_ACCT_EXPIRED int32 = 13 /* Account expired */ NSLCD_PAM_SESSION_ERR int32 = 14 /* Cannot make/remove session record */ NSLCD_PAM_AUTHTOK_ERR int32 = 20 /* Authentication token manipulation error */ NSLCD_PAM_AUTHTOK_DISABLE_AGING int32 = 23 /* Password aging disabled */ NSLCD_PAM_IGNORE int32 = 25 /* Ignore module */ NSLCD_PAM_ABORT int32 = 26 /* Fatal error */ NSLCD_PAM_AUTHTOK_EXPIRED int32 = 27 /* authentication token has expired */ )