summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@sbcglobal.net>2015-09-18 17:45:34 -0400
committerLuke Shumaker <lukeshu@sbcglobal.net>2015-09-18 17:45:34 -0400
commit34ab8645f2e08e842efdd67f8e415b6d46f184f3 (patch)
treee9d93a53da8fcb9d0b08ab444dbef376c2b3b1b2
Massive documentation and copyright clean-up.
-rw-r--r--proto/.gitignore4
-rw-r--r--proto/Makefile56
-rw-r--r--proto/doc.go22
-rw-r--r--proto/io.go172
-rw-r--r--proto/nslcd_h.go454
-rw-r--r--proto/server/doc.go23
-rwxr-xr-xproto/server/func_handlerequest.go.sh101
-rwxr-xr-xproto/server/interface_backend.go.sh39
-rwxr-xr-xproto/server/type_nilbackend.go.sh40
-rw-r--r--systemd/disable_nss_module.go60
-rw-r--r--systemd/nslcd_systemd.go165
11 files changed, 1136 insertions, 0 deletions
diff --git a/proto/.gitignore b/proto/.gitignore
new file mode 100644
index 0000000..b3f9749
--- /dev/null
+++ b/proto/.gitignore
@@ -0,0 +1,4 @@
+/requests.txt
+/server/func_handlerequest.go
+/server/interface_backend.go
+/server/type_nilbackend.go
diff --git a/proto/Makefile b/proto/Makefile
new file mode 100644
index 0000000..2335374
--- /dev/null
+++ b/proto/Makefile
@@ -0,0 +1,56 @@
+# Copyright (C) 2015 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# 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
+
+d := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))
+
+generate += $d/server/interface_backend.go
+generate += $d/server/func_handlerequest.go
+generate += $d/server/type_nilbackend.go
+secondary += $d/requests.txt
+
+ifeq (1,$(words $(MAKEFILE_LIST)))
+
+all: generate
+.PHONY: all
+
+generate: $(generate)
+.PHONY: generate
+
+clean:
+ rm -f -- $(generate) $(secondary)
+.PHONY: clean
+
+.DELETE_ON_ERROR:
+.SECONDARY:
+
+else
+
+# fix these to the current value of `d`
+generate := $(generate)
+secondary := $(secondary)
+
+endif
+
+$d/requests.txt: $d/nslcd_h.go $d/Makefile
+ < $< grep -Eo '\btype Request_([^_ ]+)(_\S+)?' | sed 's/^type Request_//' > $@
+
+%.go: %.go.sh
+ ./$^ > $@
+
+$d/server/interface_backend.go: $d/requests.txt
+$d/server/func_handlerequest.go: $d/requests.txt
+$d/server/type_nilbackend.go: $d/server/interface_backend.go
diff --git a/proto/doc.go b/proto/doc.go
new file mode 100644
index 0000000..70929f3
--- /dev/null
+++ b/proto/doc.go
@@ -0,0 +1,22 @@
+// Copyright (C) 2015 Luke Shumaker <lukeshu@sbcglobal.net>
+//
+// 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 implements the nslcd protocol of nss-pam-ldapd.
+//
+// The godoc documentation for this packages isn't very good; you
+// should look at the source file nslcd_h.go.
+package nslcd_proto
diff --git a/proto/io.go b/proto/io.go
new file mode 100644
index 0000000..3c0c5be
--- /dev/null
+++ b/proto/io.go
@@ -0,0 +1,172 @@
+// Copyright (C) 2015 Luke Shumaker <lukeshu@sbcglobal.net>
+//
+// 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 (
+ "encoding/binary"
+ "fmt"
+ "io"
+ "net"
+ "reflect"
+ "syscall"
+)
+
+type NslcdError string
+
+func (o NslcdError) Error() string {
+ return string(o)
+}
+
+type nslcdObject interface {
+ nslcdWrite(fd io.Writer)
+}
+
+type nslcdObjectPtr interface {
+ nslcdRead(fd io.Reader)
+}
+
+// Write an object to a stream. In the event of an error, this
+// function may panic! Handle it!
+func Write(fd io.Writer, data interface{}) {
+ switch data := data.(type) {
+ // basic data types
+ case nslcdObject:
+ data.nslcdWrite(fd)
+ case []byte:
+ _, err := fd.Write(data)
+ if err != nil {
+ panic(err)
+ }
+ case int32:
+ err := binary.Write(fd, binary.BigEndian, data)
+ if err != nil {
+ panic(err)
+ }
+ // composite datatypes
+ case string:
+ Write(fd, int32(len(data)))
+ Write(fd, []byte(data))
+ case []string:
+ Write(fd, int32(len(data)))
+ for _, item := range data {
+ Write(fd, item)
+ }
+ case net.IP:
+ var af int32 = -1
+ switch len(data) {
+ case net.IPv4len:
+ af = syscall.AF_INET
+ case net.IPv6len:
+ af = syscall.AF_INET6
+ }
+ var bytes []byte
+ if af < 0 {
+ bytes = make([]byte, 0)
+ } else {
+ bytes = data
+ }
+ Write(fd, af)
+ Write(fd, int32(len(bytes)))
+ Write(fd, bytes)
+ case []net.IP:
+ Write(fd, int32(len(data)))
+ for _, item := range data {
+ Write(fd, item)
+ }
+ default:
+ v := reflect.ValueOf(data)
+ switch v.Kind() {
+ case reflect.Struct:
+ for i, n := 0, v.NumField(); i < n; i++ {
+ Write(fd, v.Field(i).Interface())
+ }
+ default:
+ panic(fmt.Sprintf("Invalid structure to write NSLCD protocol data from: %T ( %#v )", data, data))
+ }
+ }
+}
+
+// Read an object from a stream. In the event of an error, this
+// function may panic! Handle it!
+func Read(fd io.Reader, data interface{}) {
+ switch data := data.(type) {
+ // basic data types
+ case nslcdObjectPtr:
+ data.nslcdRead(fd)
+ case *[]byte:
+ _, err := fd.Read(*data)
+ if err != nil {
+ panic(err)
+ }
+ case *int32:
+ err := binary.Read(fd, binary.BigEndian, data)
+ if err != nil {
+ panic(err)
+ }
+ // composite datatypes
+ case *string:
+ var len int32
+ Read(fd, &len)
+ buf := make([]byte, len)
+ Read(fd, &buf)
+ *data = string(buf)
+ case *[]string:
+ var num int32
+ Read(fd, &num)
+ *data = make([]string, num)
+ for i := 0; i < int(num); i++ {
+ Read(fd, &((*data)[i]))
+ }
+ case *net.IP:
+ var af int32
+ Read(fd, &af)
+ var _len int32
+ switch af {
+ case syscall.AF_INET:
+ _len = net.IPv4len
+ case syscall.AF_INET6:
+ _len = net.IPv6len
+ default:
+ panic(NslcdError(fmt.Sprintf("incorrect address family specified: %d", af)))
+ }
+ var len int32
+ Read(fd, &len)
+ if len != _len {
+ panic(NslcdError(fmt.Sprintf("address length incorrect: %d", len)))
+ }
+ buf := make([]byte, len)
+ Read(fd, &buf)
+ *data = buf
+ case *[]net.IP:
+ var num int32
+ Read(fd, &num)
+ *data = make([]net.IP, num)
+ for i := 0; i < int(num); i++ {
+ Read(fd, &((*data)[i]))
+ }
+ default:
+ p := reflect.ValueOf(data)
+ v := reflect.Indirect(p)
+ if p == v || v.Kind() != reflect.Struct {
+ panic(fmt.Sprintf("The argument to nslcd_proto/internal.Read() must be a pointer: %T ( %#v )", data, data))
+ }
+ for i, n := 0, v.NumField(); i < n; i++ {
+ Read(fd, v.Field(i).Addr().Interface())
+ }
+ }
+}
diff --git a/proto/nslcd_h.go b/proto/nslcd_h.go
new file mode 100644
index 0000000..cb210cd
--- /dev/null
+++ b/proto/nslcd_h.go
@@ -0,0 +1,454 @@
+// This file is based heavily on nslcd.h from nss-pam-ldapd
+// Copyright (C) 2015 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("unrecognized netgroup type")
+ }
+ 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:
+ panic(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 */
+)
diff --git a/proto/server/doc.go b/proto/server/doc.go
new file mode 100644
index 0000000..72ae2fe
--- /dev/null
+++ b/proto/server/doc.go
@@ -0,0 +1,23 @@
+// Copyright (C) 2015 Luke Shumaker <lukeshu@sbcglobal.net>
+//
+// 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_server is a framework for implementing an nslcd
+// server.
+//
+// You write an implementation of the Backend interface, then pass
+// that to the HandleRequest function for each connection.
+package nslcd_server
diff --git a/proto/server/func_handlerequest.go.sh b/proto/server/func_handlerequest.go.sh
new file mode 100755
index 0000000..3ef2758
--- /dev/null
+++ b/proto/server/func_handlerequest.go.sh
@@ -0,0 +1,101 @@
+#!/usr/bin/env bash
+# -*- Mode: Go -*-
+# Copyright (C) 2015 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# 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
+
+requests=$1
+printf '//'
+printf ' %q' "$0" "$@"
+printf '\n// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n'
+cat <<EOF | gofmt
+package nslcd_server
+
+import (
+ "fmt"
+ "io"
+ p "nslcd/proto"
+ "os"
+ s "syscall"
+)
+
+// Handle a request to nslcd
+func HandleRequest(backend Backend, in io.Reader, out io.Writer, cred s.Ucred) (err error) {
+ err = nil
+ defer func() {
+ if r := recover(); r != nil {
+ switch r := r.(type) {
+ case error:
+ err = r
+ default:
+ panic(r)
+ }
+ }
+ }()
+ handleRequest(backend, in, out, cred)
+ return
+}
+
+func handleRequest(backend Backend, in io.Reader, out io.Writer, cred s.Ucred) {
+ var version int32
+ p.Read(in, &version)
+ if version != p.NSLCD_VERSION {
+ panic(p.NslcdError(fmt.Sprintf("Version mismatch: server=%#08x client=%#08x", p.NSLCD_VERSION, version)))
+ }
+ var action int32
+ p.Read(in, &action)
+
+ ch := make(chan interface{})
+ switch action {
+$(
+while read -r request; do
+ cat <<EOT
+ case p.NSLCD_ACTION_${request^^}:
+ var req p.Request_${request}
+ p.Read(in, &req)
+ $(
+ if [[ $request == PAM_Authentication ]]; then
+ echo '_req := req'
+ echo '_req.Password = "<omitted-from-log>"'
+ echo 'fmt.Fprintf(os.Stderr, "Request: %#v\n", _req)'
+ else
+ echo 'fmt.Fprintf(os.Stderr, "Request: %#v\n", req)'
+ fi
+ )
+ _ch := backend.${request}(cred, req)
+ go func() {
+ defer close(ch)
+ for obj := range _ch {
+ ch <- obj
+ }
+ }()
+EOT
+done < "$requests"
+)
+ default:
+ close(ch)
+ panic(p.NslcdError(fmt.Sprintf("Unknown request action: %#08x", action)))
+ }
+ p.Write(out, p.NSLCD_VERSION)
+ p.Write(out, action)
+
+ for result := range ch {
+ p.Write(out, p.NSLCD_RESULT_BEGIN)
+ p.Write(out, result)
+ }
+ p.Write(out, p.NSLCD_RESULT_END)
+}
+EOF
diff --git a/proto/server/interface_backend.go.sh b/proto/server/interface_backend.go.sh
new file mode 100755
index 0000000..e6eeb66
--- /dev/null
+++ b/proto/server/interface_backend.go.sh
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+# Copyright (C) 2015 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# 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
+
+requests=$1
+printf '//'
+printf ' %q' "$0" "$@"
+printf '\n// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n'
+cat <<EOF | gofmt
+package nslcd_server
+
+import (
+ s "syscall"
+ p "nslcd/proto"
+)
+
+// The Backend interface allows the backend store to be implemented
+// separately from the protocol implementation. Each request type
+// that the nslcd server may reply to is implemented simply as a
+// method that returns a channel of the resulting values.
+type Backend interface {
+ $(sed -rn 's/([^_]+)(.*)/\1\2(s.Ucred, p.Request_\1\2) <-chan p.\1/p' "$requests" | grep -v PAM)
+ $(sed -rn 's/(PAM)(.*)/\1\2(s.Ucred, p.Request_\1\2) <-chan p.\1\2/p' "$requests")
+}
+EOF
diff --git a/proto/server/type_nilbackend.go.sh b/proto/server/type_nilbackend.go.sh
new file mode 100755
index 0000000..17aad63
--- /dev/null
+++ b/proto/server/type_nilbackend.go.sh
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+# Copyright (C) 2015 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# 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
+
+interface=$1
+printf '//'
+printf ' %q' "$0" "$@"
+printf '\n// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n'
+cat <<EOF | gofmt
+package nslcd_server
+
+import (
+ p "nslcd/proto"
+ s "syscall"
+)
+
+// NilBackend implements the Backend interface, but only returns empty
+// responses. It is useful to add as an anonymous member of a backend
+// implementation that does not return results for all of the
+// databases.
+type NilBackend struct{}
+
+$(< "$interface" sed -rn 's/^\t([^(]+)\(s.Ucred, ([^)]+)\) <-chan (\S+)$/func (o NilBackend) \1(s.Ucred, \2) <-chan \3 { r := make(chan \3); close(r); return r }/p')
+
+var _ Backend = NilBackend{}
+EOF
diff --git a/systemd/disable_nss_module.go b/systemd/disable_nss_module.go
new file mode 100644
index 0000000..c19ba0c
--- /dev/null
+++ b/systemd/disable_nss_module.go
@@ -0,0 +1,60 @@
+// Copyright (C) 2015 Luke Shumaker <lukeshu@sbcglobal.net>
+//
+// 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_systemd
+
+import (
+ "dl"
+ "sd_daemon/logger"
+ "unsafe"
+)
+
+//static char *strary(char **ary, unsigned int n) { return ary[n]; }
+import "C"
+
+const (
+ nss_module_soname = "libnss_ldap.so.2"
+ nss_module_sym_version = "_nss_ldap_version"
+ nss_module_sym_enablelookups = "_nss_ldap_enablelookups"
+)
+
+func disable_nss_module() {
+ handle, err := dl.Open(nss_module_soname, dl.RTLD_LAZY|dl.RTLD_NODELETE)
+ if err == nil {
+ defer handle.Close()
+ } else {
+ logger.Warning("NSS module %s not loaded: %v", nss_module_soname, err)
+ return
+ }
+
+ c_version_info, err := handle.Sym(nss_module_sym_version)
+ if err == nil {
+ g_version_info := (**C.char)(unsafe.Pointer(c_version_info))
+ logger.Debug("NSS module %s version %s %s", nss_module_soname,
+ C.GoString(C.strary(g_version_info, 0)),
+ C.GoString(C.strary(g_version_info, 1)))
+ } else {
+ logger.Warning("NSS module %s version missing: %v", nss_module_soname, err)
+ }
+ c_enable_flag, err := handle.Sym(nss_module_sym_enablelookups)
+ if err != nil {
+ logger.Warning("Unable to disable NSS ldap module for nslcd process: %v", err)
+ return
+ }
+ g_enable_flag := (*C.int)(unsafe.Pointer(c_enable_flag))
+ *g_enable_flag = 0
+}
diff --git a/systemd/nslcd_systemd.go b/systemd/nslcd_systemd.go
new file mode 100644
index 0000000..f5d4881
--- /dev/null
+++ b/systemd/nslcd_systemd.go
@@ -0,0 +1,165 @@
+// Copyright (C) 2015 Luke Shumaker <lukeshu@sbcglobal.net>
+//
+// 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_systemd does the legwork for implementing a systemd
+// socket-activated nslcd server.
+//
+// You just need to implement the Backend interface, then pass it to
+// Main, which will return the exit code for the process. Everything
+// but the backend is taken care of for you!
+//
+// package main
+//
+// import "nslcd/systemd"
+//
+// func main() {
+// backend := ...
+// os.Exit(int(nslcd_systemd.Main(backend)))
+// }
+package nslcd_systemd
+
+import (
+ "fmt"
+ "net"
+ "nslcd/proto/server"
+ "os"
+ "os/signal"
+ sd "sd_daemon"
+ "sd_daemon/logger"
+ "sd_daemon/lsb"
+ "sync"
+ "syscall"
+)
+
+type Backend interface {
+ nslcd_server.Backend
+ Init() error
+ Reload() error
+ Close()
+}
+
+func get_socket() (socket net.Listener, err error) {
+ socket = nil
+ err = nil
+ fds := sd.ListenFds(true)
+ if fds == nil {
+ err = fmt.Errorf("Failed to aquire sockets from systemd")
+ return
+ }
+ if len(fds) != 1 {
+ err = fmt.Errorf("Wrong number of sockets from systemd: expected %d but got %d", 1, len(fds))
+ return
+ }
+ socket, err = net.FileListener(fds[0])
+ fds[0].Close()
+ return
+}
+
+func getpeercred(conn *net.UnixConn) (cred syscall.Ucred, err error) {
+ file, err := conn.File()
+ if err != nil {
+ return
+ }
+ defer file.Close()
+ _cred, err := syscall.GetsockoptUcred(int(file.Fd()), syscall.SOL_SOCKET, syscall.SO_PEERCRED)
+ cred = *_cred
+ return
+}
+
+func handler(conn *net.UnixConn, backend nslcd_server.Backend) {
+ defer conn.Close()
+ cred, err := getpeercred(conn)
+ if err != nil {
+ logger.Debug("Connection from unknown client")
+ } else {
+ logger.Debug("Connection from pid=%v uid=%v gid=%v",
+ cred.Pid, cred.Uid, cred.Gid)
+ }
+ err = nslcd_server.HandleRequest(backend, conn, conn, cred)
+ if err != nil {
+ logger.Notice("Error while handling request: %v", err)
+ }
+}
+
+func Main(backend Backend) uint8 {
+ var err error = nil
+
+ sigs := make(chan os.Signal)
+ signal.Notify(sigs, syscall.SIGTERM, syscall.SIGHUP)
+
+ disable_nss_module()
+
+ err = backend.Init()
+ if err != nil {
+ logger.Err("Could not initialize backend: %v", err)
+ sd.Notify(false, "STOPPING=1")
+ return lsb.EXIT_FAILURE
+ }
+ defer backend.Close()
+
+ socket, err := get_socket()
+ if err != nil {
+ logger.Err("%v", err)
+ sd.Notify(false, "STOPPING=1")
+ return lsb.EXIT_NOTRUNNING
+ }
+ defer socket.Close()
+ sock := make(chan *net.UnixConn)
+ go func() {
+ defer lsb.Recover()
+ for {
+ conn, err := socket.Accept()
+ if err != nil {
+ logger.Notice("%v", err)
+ }
+ if conn != nil {
+ sock <- conn.(*net.UnixConn)
+ }
+ }
+ }()
+
+ var wg sync.WaitGroup
+ defer wg.Wait()
+ defer sd.Notify(false, "STOPPING=1")
+ sd.Notify(false, "READY=1")
+ for {
+ select {
+ case sig := <-sigs:
+ switch sig {
+ case syscall.SIGTERM:
+ logger.Notice("Received SIGTERM, shutting down")
+ return lsb.EXIT_SUCCESS
+ case syscall.SIGHUP:
+ sd.Notify(false, "RELOADING=1")
+ err := backend.Reload()
+ if err != nil {
+ logger.Notice("Could not reload backend: %s", err.Error())
+ return lsb.EXIT_NOTRUNNING
+ }
+ sd.Notify(false, "READY=1")
+ }
+ case conn := <-sock:
+ wg.Add(1)
+ go func() {
+ defer lsb.Recover()
+ defer wg.Done()
+ handler(conn, backend)
+ }()
+ }
+ }
+ panic("not reached")
+}