transaction.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. package pam
  2. //#include <security/pam_appl.h>
  3. //#include <stdlib.h>
  4. //#cgo CFLAGS: -Wall -std=c99
  5. //#cgo LDFLAGS: -lpam
  6. //struct pam_conv *make_pam_conv(void *);
  7. import "C"
  8. import (
  9. "runtime"
  10. "strings"
  11. "unsafe"
  12. )
  13. // Style is the type of message that the conversation handler should display.
  14. type Style int
  15. // Coversation handler style types.
  16. const (
  17. // PromptEchoOff indicates the conversation handler should obtain a
  18. // string without echoing any text.
  19. PromptEchoOff Style = C.PAM_PROMPT_ECHO_OFF
  20. // PromptEchoOn indicates the conversation handler should obtain a
  21. // string while echoing text.
  22. PromptEchoOn = C.PAM_PROMPT_ECHO_ON
  23. // ErrorMsg indicates the conversation handler should display an
  24. // error message.
  25. ErrorMsg = C.PAM_ERROR_MSG
  26. // TextInfo indicates the conversation handler should display some
  27. // text.
  28. TextInfo = C.PAM_TEXT_INFO
  29. )
  30. // ConversationHandler is an interface for objects that can be used as
  31. // conversation callbacks during PAM authentication.
  32. type ConversationHandler interface {
  33. // RespondPAM receives a message style and a message string. If the
  34. // message Style is PromptEchoOff or PromptEchoOn then the function
  35. // should return a response string.
  36. RespondPAM(Style, string) (string, error)
  37. }
  38. // ConversationFunc is an adapter to allow the use of ordinary functions as
  39. // conversation callbacks.
  40. type ConversationFunc func(Style, string) (string, error)
  41. // RespondPAM is a conversation callback adapter.
  42. func (f ConversationFunc) RespondPAM(s Style, msg string) (string, error) {
  43. return f(s, msg)
  44. }
  45. // Internal conversation structure
  46. type conversation struct {
  47. handler ConversationHandler
  48. conv *C.struct_pam_conv
  49. }
  50. // Constructs a new conversation object with a given handler and a newly
  51. // allocated pam_conv struct that uses this object as its appdata_ptr.
  52. func newConversation(handler ConversationHandler) (*conversation, C.int) {
  53. c := &conversation{}
  54. c.handler = handler
  55. c.conv = C.make_pam_conv(unsafe.Pointer(c))
  56. if c.conv == nil {
  57. return nil, C.PAM_BUF_ERR
  58. }
  59. return c, C.PAM_SUCCESS
  60. }
  61. // Go-side function for processing a single conversational message. Ultimately
  62. // this calls the associated ConversationHandler's ResponsePAM callback with data
  63. // coming in from a C-side call.
  64. //export cbPAMConv
  65. func cbPAMConv(s C.int, msg *C.char, appdata unsafe.Pointer) (*C.char, C.int) {
  66. c := (*conversation)(appdata)
  67. r, err := c.handler.RespondPAM(Style(s), C.GoString(msg))
  68. if err != nil {
  69. return nil, C.PAM_CONV_ERR
  70. }
  71. return C.CString(r), C.PAM_SUCCESS
  72. }
  73. // Transaction is the application's handle for a PAM transaction.
  74. type Transaction struct {
  75. handle *C.pam_handle_t
  76. conv *conversation
  77. status C.int
  78. }
  79. // Finalize a PAM transaction.
  80. func transactionFinalizer(t *Transaction) {
  81. C.pam_end(t.handle, t.status)
  82. C.free(unsafe.Pointer(t.conv.conv))
  83. }
  84. // Start initiates a new PAM transaction. Service is treated identically to
  85. // how pam_start treats it internally.
  86. //
  87. // All application calls to PAM begin with Start (or StartFunc). The returned
  88. // transaction provides an interface to the remainder of the API.
  89. func Start(service, user string, handler ConversationHandler) (*Transaction, error) {
  90. t := &Transaction{}
  91. t.conv, t.status = newConversation(handler)
  92. if t.status != C.PAM_SUCCESS {
  93. return nil, t
  94. }
  95. s := C.CString(service)
  96. defer C.free(unsafe.Pointer(s))
  97. var u *C.char
  98. if len(user) != 0 {
  99. u = C.CString(user)
  100. defer C.free(unsafe.Pointer(u))
  101. }
  102. t.status = C.pam_start(s, u, t.conv.conv, &t.handle)
  103. if t.status != C.PAM_SUCCESS {
  104. C.free(unsafe.Pointer(t.conv.conv))
  105. return nil, t
  106. }
  107. runtime.SetFinalizer(t, transactionFinalizer)
  108. return t, nil
  109. }
  110. // StartFunc registers the handler func as a conversation handler.
  111. func StartFunc(service, user string, handler func(Style, string) (string, error)) (*Transaction, error) {
  112. return Start(service, user, ConversationFunc(handler))
  113. }
  114. func (t *Transaction) Error() string {
  115. return C.GoString(C.pam_strerror(t.handle, C.int(t.status)))
  116. }
  117. // Item is a an PAM information type.
  118. type Item int
  119. // PAM Item types.
  120. const (
  121. // Service is the name which identifies the PAM stack.
  122. Service Item = C.PAM_SERVICE
  123. // User identifies the username identity used by a service.
  124. User = C.PAM_USER
  125. // Tty is the terminal name.
  126. Tty = C.PAM_TTY
  127. // Rhost is the requesting host name.
  128. Rhost = C.PAM_RHOST
  129. // Authtok is the currently active authentication token.
  130. Authtok = C.PAM_AUTHTOK
  131. // Oldauthtok is the old authentication token.
  132. Oldauthtok = C.PAM_OLDAUTHTOK
  133. // Ruser is the requesting user name.
  134. Ruser = C.PAM_RUSER
  135. // UserPrompt is the string use to prompt for a username.
  136. UserPrompt = C.PAM_USER_PROMPT
  137. )
  138. // SetItem sets a PAM information item.
  139. func (t *Transaction) SetItem(i Item, item string) error {
  140. cs := unsafe.Pointer(C.CString(item))
  141. defer C.free(cs)
  142. t.status = C.pam_set_item(t.handle, C.int(i), cs)
  143. if t.status != C.PAM_SUCCESS {
  144. return t
  145. }
  146. return nil
  147. }
  148. // GetItem retrieves a PAM information item.
  149. func (t *Transaction) GetItem(i Item) (string, error) {
  150. var s unsafe.Pointer
  151. t.status = C.pam_get_item(t.handle, C.int(i), &s)
  152. if t.status != C.PAM_SUCCESS {
  153. return "", t
  154. }
  155. return C.GoString((*C.char)(s)), nil
  156. }
  157. // Flags are inputs to various PAM functions than be combined with a bitwise
  158. // or. Refer to the official PAM documentation for which flags are accepted
  159. // by which functions.
  160. type Flags int
  161. // PAM Flag types.
  162. const (
  163. // Silent indicates that no messages should be emitted.
  164. Silent Flags = C.PAM_SILENT
  165. // DisallowNullAuthtok indicates that authorization should fail
  166. // if the user does not have a registered authentication token.
  167. DisallowNullAuthtok = C.PAM_DISALLOW_NULL_AUTHTOK
  168. // EstablishCred indicates that credentials should be established
  169. // for the user.
  170. EstablishCred = C.PAM_ESTABLISH_CRED
  171. // DeleteCred inidicates that credentials should be deleted.
  172. DeleteCred = C.PAM_DELETE_CRED
  173. // ReinitializeCred indicates that credentials should be fully
  174. // reinitialized.
  175. ReinitializeCred = C.PAM_REINITIALIZE_CRED
  176. // RefreshCred indicates that the lifetime of existing credentials
  177. // should be extended.
  178. RefreshCred = C.PAM_REFRESH_CRED
  179. // ChangeExpiredAuthtok indicates that the authentication token
  180. // should be changed if it has expired.
  181. ChangeExpiredAuthtok = C.PAM_CHANGE_EXPIRED_AUTHTOK
  182. )
  183. // Authenticate is used to authenticate the user.
  184. //
  185. // Valid flags: Silent, DisallowNullAuthtok
  186. func (t *Transaction) Authenticate(f Flags) error {
  187. t.status = C.pam_authenticate(t.handle, C.int(f))
  188. if t.status != C.PAM_SUCCESS {
  189. return t
  190. }
  191. return nil
  192. }
  193. // SetCred is used to establish, maintain and delete the credentials of a
  194. // user.
  195. //
  196. // Valid flags: EstablishCred, DeleteCred, ReinitializeCred, RefreshCred
  197. func (t *Transaction) SetCred(f Flags) error {
  198. t.status = C.pam_setcred(t.handle, C.int(f))
  199. if t.status != C.PAM_SUCCESS {
  200. return t
  201. }
  202. return nil
  203. }
  204. // AcctMgmt is used to determine if the user's account is valid.
  205. //
  206. // Valid flags: Silent, DisallowNullAuthtok
  207. func (t *Transaction) AcctMgmt(f Flags) error {
  208. t.status = C.pam_acct_mgmt(t.handle, C.int(f))
  209. if t.status != C.PAM_SUCCESS {
  210. return t
  211. }
  212. return nil
  213. }
  214. // ChangeAuthTok is used to change the authentication token.
  215. //
  216. // Valid flags: Silent, ChangeExpiredAuthtok
  217. func (t *Transaction) ChangeAuthTok(f Flags) error {
  218. t.status = C.pam_chauthtok(t.handle, C.int(f))
  219. if t.status != C.PAM_SUCCESS {
  220. return t
  221. }
  222. return nil
  223. }
  224. // OpenSession sets up a user session for an authenticated user.
  225. //
  226. // Valid flags: Slient
  227. func (t *Transaction) OpenSession(f Flags) error {
  228. t.status = C.pam_open_session(t.handle, C.int(f))
  229. if t.status != C.PAM_SUCCESS {
  230. return t
  231. }
  232. return nil
  233. }
  234. // CloseSession closes a previously opened session.
  235. //
  236. // Valid flags: Silent
  237. func (t *Transaction) CloseSession(f Flags) error {
  238. t.status = C.pam_close_session(t.handle, C.int(f))
  239. if t.status != C.PAM_SUCCESS {
  240. return t
  241. }
  242. return nil
  243. }
  244. // PutEnv adds or changes the value of PAM environment variables.
  245. //
  246. // NAME=value will set a variable to a value.
  247. // NAME= will set a variable to an empty value.
  248. // NAME (without an "=") will delete a variable.
  249. func (t *Transaction) PutEnv(nameval string) error {
  250. cs := C.CString(nameval)
  251. defer C.free(unsafe.Pointer(cs))
  252. t.status = C.pam_putenv(t.handle, cs)
  253. if t.status != C.PAM_SUCCESS {
  254. return t
  255. }
  256. return nil
  257. }
  258. // GetEnv is used to retrieve a PAM environment variable.
  259. func (t *Transaction) GetEnv(name string) string {
  260. cs := C.CString(name)
  261. defer C.free(unsafe.Pointer(cs))
  262. value := C.pam_getenv(t.handle, cs)
  263. if value == nil {
  264. return ""
  265. }
  266. return C.GoString(value)
  267. }
  268. func next(p **C.char) **C.char {
  269. return (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + unsafe.Sizeof(p)))
  270. }
  271. // GetEnvList returns a copy of the PAM environment as a map.
  272. func (t *Transaction) GetEnvList() (map[string]string, error) {
  273. env := make(map[string]string)
  274. p := C.pam_getenvlist(t.handle)
  275. if p == nil {
  276. t.status = C.PAM_BUF_ERR
  277. return nil, t
  278. }
  279. for q := p; *q != nil; q = next(q) {
  280. chunks := strings.SplitN(C.GoString(*q), "=", 2)
  281. if len(chunks) == 2 {
  282. env[chunks[0]] = chunks[1]
  283. }
  284. C.free(unsafe.Pointer(*q))
  285. }
  286. C.free(unsafe.Pointer(p))
  287. return env, nil
  288. }