client_integration_test.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. // +build integration
  2. // To turn on this test use -tags=integration in go test command
  3. package client
  4. import (
  5. "bytes"
  6. "encoding/hex"
  7. "io"
  8. "io/ioutil"
  9. "net/http"
  10. "os"
  11. "os/exec"
  12. "os/user"
  13. "runtime"
  14. "testing"
  15. "time"
  16. "errors"
  17. "fmt"
  18. "github.com/stretchr/testify/assert"
  19. "gopkg.in/jcmturner/gokrb5.v6/config"
  20. "gopkg.in/jcmturner/gokrb5.v6/credentials"
  21. "gopkg.in/jcmturner/gokrb5.v6/iana/etypeID"
  22. "gopkg.in/jcmturner/gokrb5.v6/keytab"
  23. "gopkg.in/jcmturner/gokrb5.v6/testdata"
  24. "strings"
  25. "sync"
  26. )
  27. func TestClient_SuccessfulLogin_Keytab(t *testing.T) {
  28. addr := os.Getenv("TEST_KDC_ADDR")
  29. if addr == "" {
  30. addr = testdata.TEST_KDC_ADDR
  31. }
  32. b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
  33. kt, _ := keytab.Parse(b)
  34. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  35. var tests = []string{
  36. testdata.TEST_KDC,
  37. testdata.TEST_KDC_OLD,
  38. testdata.TEST_KDC_LASTEST,
  39. }
  40. for _, test := range tests {
  41. c.Realms[0].KDC = []string{addr + ":" + test}
  42. cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
  43. cl.WithConfig(c)
  44. err := cl.Login()
  45. if err != nil {
  46. t.Errorf("error on logging in with KDC %s: %v\n", test, err)
  47. }
  48. }
  49. }
  50. func TestClient_SuccessfulLogin_Password(t *testing.T) {
  51. addr := os.Getenv("TEST_KDC_ADDR")
  52. if addr == "" {
  53. addr = testdata.TEST_KDC_ADDR
  54. }
  55. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  56. var tests = []string{
  57. testdata.TEST_KDC,
  58. testdata.TEST_KDC_OLD,
  59. testdata.TEST_KDC_LASTEST,
  60. }
  61. for _, test := range tests {
  62. c.Realms[0].KDC = []string{addr + ":" + test}
  63. cl := NewClientWithPassword("testuser1", "TEST.GOKRB5", "passwordvalue")
  64. cl.WithConfig(c)
  65. err := cl.Login()
  66. if err != nil {
  67. t.Errorf("error on logging in with KDC %s: %v\n", test, err)
  68. }
  69. }
  70. }
  71. func TestClient_SuccessfulLogin_TCPOnly(t *testing.T) {
  72. b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
  73. kt, _ := keytab.Parse(b)
  74. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  75. addr := os.Getenv("TEST_KDC_ADDR")
  76. if addr == "" {
  77. addr = testdata.TEST_KDC_ADDR
  78. }
  79. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
  80. c.LibDefaults.UDPPreferenceLimit = 1
  81. cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
  82. cl.WithConfig(c)
  83. err := cl.Login()
  84. if err != nil {
  85. t.Fatalf("error on login: %v\n", err)
  86. }
  87. }
  88. func TestClient_ASExchange_TGSExchange_EncTypes_Keytab(t *testing.T) {
  89. b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
  90. kt, _ := keytab.Parse(b)
  91. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  92. addr := os.Getenv("TEST_KDC_ADDR")
  93. if addr == "" {
  94. addr = testdata.TEST_KDC_ADDR
  95. }
  96. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_LASTEST}
  97. var tests = []string{
  98. "des3-cbc-sha1-kd",
  99. "aes128-cts-hmac-sha1-96",
  100. "aes256-cts-hmac-sha1-96",
  101. "aes128-cts-hmac-sha256-128",
  102. "aes256-cts-hmac-sha384-192",
  103. "rc4-hmac",
  104. }
  105. for _, test := range tests {
  106. c.LibDefaults.DefaultTktEnctypes = []string{test}
  107. c.LibDefaults.DefaultTktEnctypeIDs = []int32{etypeID.ETypesByName[test]}
  108. c.LibDefaults.DefaultTGSEnctypes = []string{test}
  109. c.LibDefaults.DefaultTGSEnctypeIDs = []int32{etypeID.ETypesByName[test]}
  110. cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
  111. cl.WithConfig(c)
  112. err := cl.Login()
  113. if err != nil {
  114. t.Errorf("error on login using enctype %s: %v\n", test, err)
  115. }
  116. tkt, key, err := cl.GetServiceTicket("HTTP/host.test.gokrb5")
  117. if err != nil {
  118. t.Errorf("error in TGS exchange using enctype %s: %v", test, err)
  119. }
  120. assert.Equal(t, "TEST.GOKRB5", tkt.Realm, "Realm in ticket not as expected for %s test", test)
  121. assert.Equal(t, etypeID.ETypesByName[test], key.KeyType, "Key is not for enctype %s", test)
  122. }
  123. }
  124. func TestClient_ASExchange_TGSExchange_EncTypes_Password(t *testing.T) {
  125. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  126. addr := os.Getenv("TEST_KDC_ADDR")
  127. if addr == "" {
  128. addr = testdata.TEST_KDC_ADDR
  129. }
  130. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_LASTEST}
  131. var tests = []string{
  132. "des3-cbc-sha1-kd",
  133. "aes128-cts-hmac-sha1-96",
  134. "aes256-cts-hmac-sha1-96",
  135. "aes128-cts-hmac-sha256-128",
  136. "aes256-cts-hmac-sha384-192",
  137. "rc4-hmac",
  138. }
  139. for _, test := range tests {
  140. c.LibDefaults.DefaultTktEnctypes = []string{test}
  141. c.LibDefaults.DefaultTktEnctypeIDs = []int32{etypeID.ETypesByName[test]}
  142. c.LibDefaults.DefaultTGSEnctypes = []string{test}
  143. c.LibDefaults.DefaultTGSEnctypeIDs = []int32{etypeID.ETypesByName[test]}
  144. cl := NewClientWithPassword("testuser1", "TEST.GOKRB5", "passwordvalue")
  145. cl.WithConfig(c)
  146. err := cl.Login()
  147. if err != nil {
  148. t.Errorf("error on login using enctype %s: %v\n", test, err)
  149. }
  150. tkt, key, err := cl.GetServiceTicket("HTTP/host.test.gokrb5")
  151. if err != nil {
  152. t.Errorf("error in TGS exchange using enctype %s: %v", test, err)
  153. }
  154. assert.Equal(t, "TEST.GOKRB5", tkt.Realm, "Realm in ticket not as expected for %s test", test)
  155. assert.Equal(t, etypeID.ETypesByName[test], key.KeyType, "Key is not for enctype %s", test)
  156. }
  157. }
  158. func TestClient_FailedLogin(t *testing.T) {
  159. b, _ := hex.DecodeString(testdata.TESTUSER1_WRONGPASSWD)
  160. kt, _ := keytab.Parse(b)
  161. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  162. addr := os.Getenv("TEST_KDC_ADDR")
  163. if addr == "" {
  164. addr = testdata.TEST_KDC_ADDR
  165. }
  166. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
  167. cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
  168. cl.WithConfig(c)
  169. err := cl.Login()
  170. if err == nil {
  171. t.Fatal("login with incorrect password did not error")
  172. }
  173. }
  174. func TestClient_SuccessfulLogin_UserRequiringPreAuth(t *testing.T) {
  175. b, _ := hex.DecodeString(testdata.TESTUSER2_KEYTAB)
  176. kt, _ := keytab.Parse(b)
  177. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  178. addr := os.Getenv("TEST_KDC_ADDR")
  179. if addr == "" {
  180. addr = testdata.TEST_KDC_ADDR
  181. }
  182. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
  183. cl := NewClientWithKeytab("testuser2", "TEST.GOKRB5", kt)
  184. cl.WithConfig(c)
  185. err := cl.Login()
  186. if err != nil {
  187. t.Fatalf("error on login: %v\n", err)
  188. }
  189. }
  190. func TestClient_SuccessfulLogin_UserRequiringPreAuth_TCPOnly(t *testing.T) {
  191. b, _ := hex.DecodeString(testdata.TESTUSER2_KEYTAB)
  192. kt, _ := keytab.Parse(b)
  193. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  194. addr := os.Getenv("TEST_KDC_ADDR")
  195. if addr == "" {
  196. addr = testdata.TEST_KDC_ADDR
  197. }
  198. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
  199. c.LibDefaults.UDPPreferenceLimit = 1
  200. cl := NewClientWithKeytab("testuser2", "TEST.GOKRB5", kt)
  201. cl.WithConfig(c)
  202. err := cl.Login()
  203. if err != nil {
  204. t.Fatalf("error on login: %v\n", err)
  205. }
  206. }
  207. func TestClient_NetworkTimeout(t *testing.T) {
  208. b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
  209. kt, _ := keytab.Parse(b)
  210. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  211. c.Realms[0].KDC = []string{testdata.TEST_KDC_BADADDR + ":88"}
  212. cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
  213. cl.WithConfig(c)
  214. err := cl.Login()
  215. if err == nil {
  216. t.Fatal("login with incorrect KDC address did not error")
  217. }
  218. }
  219. func TestClient_GetServiceTicket(t *testing.T) {
  220. b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
  221. kt, _ := keytab.Parse(b)
  222. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  223. addr := os.Getenv("TEST_KDC_ADDR")
  224. if addr == "" {
  225. addr = testdata.TEST_KDC_ADDR
  226. }
  227. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
  228. cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
  229. cl.WithConfig(c)
  230. err := cl.Login()
  231. if err != nil {
  232. t.Fatalf("error on login: %v\n", err)
  233. }
  234. spn := "HTTP/host.test.gokrb5"
  235. tkt, key, err := cl.GetServiceTicket(spn)
  236. if err != nil {
  237. t.Fatalf("error getting service ticket: %v\n", err)
  238. }
  239. assert.Equal(t, spn, tkt.SName.GetPrincipalNameString())
  240. assert.Equal(t, int32(18), key.KeyType)
  241. //Check cache use - should get the same values back again
  242. tkt2, key2, err := cl.GetServiceTicket(spn)
  243. if err != nil {
  244. t.Fatalf("error getting service ticket: %v\n", err)
  245. }
  246. assert.Equal(t, tkt.EncPart.Cipher, tkt2.EncPart.Cipher)
  247. assert.Equal(t, key.KeyValue, key2.KeyValue)
  248. }
  249. func TestClient_GetServiceTicket_InvalidSPN(t *testing.T) {
  250. b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
  251. kt, _ := keytab.Parse(b)
  252. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  253. addr := os.Getenv("TEST_KDC_ADDR")
  254. if addr == "" {
  255. addr = testdata.TEST_KDC_ADDR
  256. }
  257. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
  258. cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
  259. cl.WithConfig(c)
  260. err := cl.Login()
  261. if err != nil {
  262. t.Fatalf("error on login: %v\n", err)
  263. }
  264. spn := "host.test.gokrb5"
  265. _, _, err = cl.GetServiceTicket(spn)
  266. assert.NotNil(t, err, "Expected unknown principal error")
  267. assert.True(t, strings.Contains(err.Error(), "KDC_ERR_S_PRINCIPAL_UNKNOWN"), "Error text not as expected")
  268. }
  269. func TestClient_GetServiceTicket_OlderKDC(t *testing.T) {
  270. b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
  271. kt, _ := keytab.Parse(b)
  272. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  273. addr := os.Getenv("TEST_KDC_ADDR")
  274. if addr == "" {
  275. addr = testdata.TEST_KDC_ADDR
  276. }
  277. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_OLD}
  278. cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
  279. cl.WithConfig(c)
  280. err := cl.Login()
  281. if err != nil {
  282. t.Fatalf("error on login: %v\n", err)
  283. }
  284. spn := "HTTP/host.test.gokrb5"
  285. tkt, key, err := cl.GetServiceTicket(spn)
  286. if err != nil {
  287. t.Fatalf("error getting service ticket: %v\n", err)
  288. }
  289. assert.Equal(t, spn, tkt.SName.GetPrincipalNameString())
  290. assert.Equal(t, int32(18), key.KeyType)
  291. }
  292. func TestClient_SetSPNEGOHeader(t *testing.T) {
  293. b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
  294. kt, _ := keytab.Parse(b)
  295. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  296. addr := os.Getenv("TEST_KDC_ADDR")
  297. if addr == "" {
  298. addr = testdata.TEST_KDC_ADDR
  299. }
  300. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
  301. cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
  302. cl.WithConfig(c)
  303. err := cl.Login()
  304. if err != nil {
  305. t.Fatalf("error on AS_REQ: %v\n", err)
  306. }
  307. url := os.Getenv("TEST_HTTP_URL")
  308. if url == "" {
  309. url = testdata.TEST_HTTP_URL
  310. }
  311. paths := []string{
  312. "/modkerb/index.html",
  313. "/modgssapi/index.html",
  314. }
  315. for _, p := range paths {
  316. r, _ := http.NewRequest("GET", url+p, nil)
  317. httpResp, err := http.DefaultClient.Do(r)
  318. if err != nil {
  319. t.Fatalf("%s request error: %v\n", url+p, err)
  320. }
  321. assert.Equal(t, http.StatusUnauthorized, httpResp.StatusCode, "Status code in response to client with no SPNEGO not as expected")
  322. err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5")
  323. if err != nil {
  324. t.Fatalf("error setting client SPNEGO header: %v", err)
  325. }
  326. httpResp, err = http.DefaultClient.Do(r)
  327. if err != nil {
  328. t.Fatalf("%s request error: %v\n", url+p, err)
  329. }
  330. assert.Equal(t, http.StatusOK, httpResp.StatusCode, "Status code in response to client SPNEGO request not as expected")
  331. }
  332. }
  333. func TestMultiThreadedClientUse(t *testing.T) {
  334. b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
  335. kt, _ := keytab.Parse(b)
  336. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  337. addr := os.Getenv("TEST_KDC_ADDR")
  338. if addr == "" {
  339. addr = testdata.TEST_KDC_ADDR
  340. }
  341. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
  342. cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
  343. cl.WithConfig(c)
  344. var wg sync.WaitGroup
  345. wg.Add(5)
  346. for i := 0; i < 5; i++ {
  347. go func() {
  348. defer wg.Done()
  349. err := cl.Login()
  350. if err != nil {
  351. panic(err)
  352. }
  353. }()
  354. }
  355. wg.Wait()
  356. var wg2 sync.WaitGroup
  357. wg2.Add(5)
  358. for i := 0; i < 5; i++ {
  359. go func() {
  360. defer wg2.Done()
  361. err := spnegoGet(&cl)
  362. if err != nil {
  363. panic(err)
  364. }
  365. }()
  366. }
  367. wg2.Wait()
  368. }
  369. func TestMultiThreadedClientSession(t *testing.T) {
  370. b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
  371. kt, _ := keytab.Parse(b)
  372. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  373. addr := os.Getenv("TEST_KDC_ADDR")
  374. if addr == "" {
  375. addr = testdata.TEST_KDC_ADDR
  376. }
  377. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
  378. cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
  379. cl.WithConfig(c)
  380. err := cl.Login()
  381. if err != nil {
  382. t.Fatalf("failed to log in: %v", err)
  383. }
  384. s, ok := cl.sessions.get("TEST.GOKRB5")
  385. if !ok {
  386. t.Fatal("error initially getting session")
  387. }
  388. go func() {
  389. for {
  390. err := cl.renewTGT(s)
  391. if err != nil {
  392. t.Logf("error renewing TGT: %v", err)
  393. }
  394. time.Sleep(time.Millisecond * 100)
  395. }
  396. }()
  397. var wg sync.WaitGroup
  398. wg.Add(10)
  399. for i := 0; i < 10; i++ {
  400. go func() {
  401. defer wg.Done()
  402. tgt, _, err := cl.sessionTGT("TEST.GOKRB5")
  403. if err != nil || tgt.Realm != "TEST.GOKRB5" {
  404. t.Logf("error getting session: %v", err)
  405. }
  406. _, _, _, r, _ := cl.sessionTimes("TEST.GOKRB5")
  407. fmt.Fprintf(ioutil.Discard, "%v", r)
  408. }()
  409. time.Sleep(time.Second)
  410. }
  411. wg.Wait()
  412. }
  413. func spnegoGet(cl *Client) error {
  414. url := os.Getenv("TEST_HTTP_URL")
  415. if url == "" {
  416. url = testdata.TEST_HTTP_URL
  417. }
  418. r, _ := http.NewRequest("GET", url+"/modgssapi/index.html", nil)
  419. httpResp, err := http.DefaultClient.Do(r)
  420. if err != nil {
  421. return fmt.Errorf("request error: %v\n", err)
  422. }
  423. if httpResp.StatusCode != http.StatusUnauthorized {
  424. return errors.New("did not get unauthorized code when no SPNEGO header set")
  425. }
  426. err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5")
  427. if err != nil {
  428. return fmt.Errorf("error setting client SPNEGO header: %v", err)
  429. }
  430. httpResp, err = http.DefaultClient.Do(r)
  431. if err != nil {
  432. return fmt.Errorf("request error: %v\n", err)
  433. }
  434. if httpResp.StatusCode != http.StatusOK {
  435. return errors.New("did not get OK code when SPNEGO header set")
  436. }
  437. return nil
  438. }
  439. func TestNewClientFromCCache(t *testing.T) {
  440. b, err := hex.DecodeString(testdata.CCACHE_TEST)
  441. if err != nil {
  442. t.Fatalf("error decoding test data")
  443. }
  444. cc, err := credentials.ParseCCache(b)
  445. if err != nil {
  446. t.Fatal("error getting test CCache")
  447. }
  448. cl, err := NewClientFromCCache(cc)
  449. if err != nil {
  450. t.Fatalf("error creating client from CCache: %v", err)
  451. }
  452. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  453. addr := os.Getenv("TEST_KDC_ADDR")
  454. if addr == "" {
  455. addr = testdata.TEST_KDC_ADDR
  456. }
  457. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
  458. cl.WithConfig(c)
  459. if ok, err := cl.IsConfigured(); !ok {
  460. t.Fatalf("client was not configured from CCache: %v", err)
  461. }
  462. }
  463. // Login to the TEST.GOKRB5 domain and request service ticket for resource in the RESDOM.GOKRB5 domain.
  464. // There is a trust between the two domains.
  465. func TestClient_GetServiceTicket_Trusted_Resource_Domain(t *testing.T) {
  466. b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
  467. kt, _ := keytab.Parse(b)
  468. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  469. addr := os.Getenv("TEST_KDC_ADDR")
  470. if addr == "" {
  471. addr = testdata.TEST_KDC_ADDR
  472. }
  473. for i, r := range c.Realms {
  474. if r.Realm == "TEST.GOKRB5" {
  475. c.Realms[i].KDC = []string{addr + ":" + testdata.TEST_KDC}
  476. }
  477. if r.Realm == "RESDOM.GOKRB5" {
  478. c.Realms[i].KDC = []string{addr + ":" + testdata.TEST_KDC_RESDOM}
  479. }
  480. }
  481. c.LibDefaults.DefaultRealm = "TEST.GOKRB5"
  482. cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
  483. c.LibDefaults.DefaultTktEnctypes = []string{"aes256-cts-hmac-sha1-96"}
  484. c.LibDefaults.DefaultTktEnctypeIDs = []int32{etypeID.ETypesByName["aes256-cts-hmac-sha1-96"]}
  485. c.LibDefaults.DefaultTGSEnctypes = []string{"aes256-cts-hmac-sha1-96"}
  486. c.LibDefaults.DefaultTGSEnctypeIDs = []int32{etypeID.ETypesByName["aes256-cts-hmac-sha1-96"]}
  487. cl.WithConfig(c)
  488. err := cl.Login()
  489. if err != nil {
  490. t.Fatalf("error on login: %v\n", err)
  491. }
  492. spn := "HTTP/host.resdom.gokrb5"
  493. tkt, key, err := cl.GetServiceTicket(spn)
  494. if err != nil {
  495. t.Fatalf("error getting service ticket: %v\n", err)
  496. }
  497. assert.Equal(t, spn, tkt.SName.GetPrincipalNameString())
  498. assert.Equal(t, etypeID.ETypesByName["aes256-cts-hmac-sha1-96"], key.KeyType)
  499. b, _ = hex.DecodeString(testdata.SYSHTTP_RESDOM_KEYTAB)
  500. skt, _ := keytab.Parse(b)
  501. err = tkt.DecryptEncPart(skt, "")
  502. if err != nil {
  503. t.Errorf("error decrypting ticket with service keytab: %v", err)
  504. }
  505. }
  506. const (
  507. kinitCmd = "kinit"
  508. kvnoCmd = "kvno"
  509. spn = "HTTP/host.test.gokrb5"
  510. )
  511. func login() error {
  512. file, err := os.Create("/etc/krb5.conf")
  513. if err != nil {
  514. return fmt.Errorf("cannot open krb5.conf: %v", err)
  515. }
  516. defer file.Close()
  517. fmt.Fprintf(file, testdata.TEST_KRB5CONF)
  518. cmd := exec.Command(kinitCmd, "testuser1@TEST.GOKRB5")
  519. stdinR, stdinW := io.Pipe()
  520. stderrR, stderrW := io.Pipe()
  521. cmd.Stdin = stdinR
  522. cmd.Stderr = stderrW
  523. err = cmd.Start()
  524. if err != nil {
  525. return fmt.Errorf("could not start %s command: %v", kinitCmd, err)
  526. }
  527. go func() {
  528. io.WriteString(stdinW, "passwordvalue")
  529. stdinW.Close()
  530. }()
  531. errBuf := new(bytes.Buffer)
  532. go func() {
  533. io.Copy(errBuf, stderrR)
  534. stderrR.Close()
  535. }()
  536. err = cmd.Wait()
  537. if err != nil {
  538. return fmt.Errorf("%s did not run successfully: %v stderr: %s", kinitCmd, err, string(errBuf.Bytes()))
  539. }
  540. return nil
  541. }
  542. func getServiceTkt() error {
  543. cmd := exec.Command(kvnoCmd, spn)
  544. err := cmd.Start()
  545. if err != nil {
  546. return fmt.Errorf("could not start %s command: %v", kvnoCmd, err)
  547. }
  548. err = cmd.Wait()
  549. if err != nil {
  550. return fmt.Errorf("%s did not run successfully: %v", kvnoCmd, err)
  551. }
  552. return nil
  553. }
  554. func loadCCache() (credentials.CCache, error) {
  555. usr, _ := user.Current()
  556. cpath := "/tmp/krb5cc_" + usr.Uid
  557. return credentials.LoadCCache(cpath)
  558. }
  559. func TestGetServiceTicketFromCCacheTGT(t *testing.T) {
  560. err := login()
  561. if err != nil {
  562. t.Fatalf("error logging in with kinit: %v", err)
  563. }
  564. c, err := loadCCache()
  565. if err != nil {
  566. t.Errorf("error loading CCache: %v", err)
  567. }
  568. cfg, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  569. addr := os.Getenv("TEST_KDC_ADDR")
  570. if addr == "" {
  571. addr = testdata.TEST_KDC_ADDR
  572. }
  573. cfg.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
  574. cl, err := NewClientFromCCache(c)
  575. if err != nil {
  576. t.Fatalf("error generating client from ccache: %v", err)
  577. }
  578. cl.WithConfig(cfg)
  579. url := os.Getenv("TEST_HTTP_URL")
  580. if url == "" {
  581. url = testdata.TEST_HTTP_URL
  582. }
  583. r, _ := http.NewRequest("GET", url+"/modgssapi/index.html", nil)
  584. err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5")
  585. if err != nil {
  586. t.Fatalf("error setting client SPNEGO header: %v", err)
  587. }
  588. httpResp, err := http.DefaultClient.Do(r)
  589. if err != nil {
  590. t.Fatalf("request error: %v\n", err)
  591. }
  592. assert.Equal(t, http.StatusOK, httpResp.StatusCode, "status code in response to client SPNEGO request not as expected")
  593. }
  594. func TestGetServiceTicketFromCCacheWithoutKDC(t *testing.T) {
  595. err := login()
  596. if err != nil {
  597. t.Fatalf("error logging in with kinit: %v", err)
  598. }
  599. err = getServiceTkt()
  600. if err != nil {
  601. t.Fatalf("error getting service ticket: %v", err)
  602. }
  603. c, err := loadCCache()
  604. if err != nil {
  605. t.Errorf("error loading CCache: %v", err)
  606. }
  607. cfg, _ := config.NewConfigFromString("...")
  608. cl, err := NewClientFromCCache(c)
  609. if err != nil {
  610. t.Fatalf("error generating client from ccache: %v", err)
  611. }
  612. cl.WithConfig(cfg)
  613. url := os.Getenv("TEST_HTTP_URL")
  614. if url == "" {
  615. url = testdata.TEST_HTTP_URL
  616. }
  617. r, _ := http.NewRequest("GET", url+"/modgssapi/index.html", nil)
  618. err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5")
  619. if err != nil {
  620. t.Fatalf("error setting client SPNEGO header: %v", err)
  621. }
  622. httpResp, err := http.DefaultClient.Do(r)
  623. if err != nil {
  624. t.Fatalf("request error: %v\n", err)
  625. }
  626. assert.Equal(t, http.StatusOK, httpResp.StatusCode, "status code in response to client SPNEGO request not as expected")
  627. }
  628. func TestClient_ChangePasswd(t *testing.T) {
  629. b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
  630. kt, _ := keytab.Parse(b)
  631. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  632. addr := os.Getenv("TEST_KDC_ADDR")
  633. if addr == "" {
  634. addr = testdata.TEST_KDC_ADDR
  635. }
  636. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
  637. c.Realms[0].KPasswdServer = []string{addr + ":464"}
  638. cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
  639. cl.WithConfig(c)
  640. ok, err := cl.ChangePasswd("newpassword")
  641. if err != nil {
  642. t.Fatalf("error changing password: %v", err)
  643. }
  644. assert.True(t, ok, "password was not changed")
  645. cl = NewClientWithPassword("testuser1", "TEST.GOKRB5", "newpassword")
  646. cl.WithConfig(c)
  647. ok, err = cl.ChangePasswd(testdata.TESTUSER1_PASSWORD)
  648. if err != nil {
  649. t.Fatalf("error changing password: %v", err)
  650. }
  651. assert.True(t, ok, "password was not changed back")
  652. }
  653. func TestClient_AutoRenew_Goroutine(t *testing.T) {
  654. // Tests that the auto renew of client credentials is not spawning goroutines out of control.
  655. addr := os.Getenv("TEST_KDC_ADDR")
  656. if addr == "" {
  657. addr = testdata.TEST_KDC_ADDR
  658. }
  659. b, _ := hex.DecodeString(testdata.TESTUSER2_KEYTAB)
  660. kt, _ := keytab.Parse(b)
  661. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  662. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_SHORTTICKETS}
  663. c.LibDefaults.PreferredPreauthTypes = []int{int(etypeID.DES3_CBC_SHA1_KD)} // a preauth etype the KDC does not support. Test this does not cause renewal to fail.
  664. cl := NewClientWithKeytab("testuser2", "TEST.GOKRB5", kt)
  665. cl.WithConfig(c)
  666. err := cl.Login()
  667. if err != nil {
  668. t.Errorf("error on logging in: %v\n", err)
  669. }
  670. n := runtime.NumGoroutine()
  671. for i := 0; i < 24; i++ {
  672. time.Sleep(time.Second * 5)
  673. _, endTime, _, _, err := cl.sessionTimes("TEST.GOKRB5")
  674. if err != nil {
  675. t.Errorf("could not get client's session: %v", err)
  676. }
  677. if time.Now().UTC().After(endTime) {
  678. t.Fatalf("session auto update failed")
  679. }
  680. spn := "HTTP/host.test.gokrb5"
  681. tkt, key, err := cl.GetServiceTicket(spn)
  682. if err != nil {
  683. t.Fatalf("error getting service ticket: %v\n", err)
  684. }
  685. b, _ := hex.DecodeString(testdata.HTTP_KEYTAB)
  686. skt, _ := keytab.Parse(b)
  687. tkt.DecryptEncPart(skt, "")
  688. assert.Equal(t, spn, tkt.SName.GetPrincipalNameString())
  689. assert.Equal(t, int32(18), key.KeyType)
  690. if runtime.NumGoroutine() > n {
  691. t.Fatalf("number of goroutines is increasing: should not be more than %d, is %d", n, runtime.NumGoroutine())
  692. }
  693. }
  694. }
  695. func TestClient_Destroy(t *testing.T) {
  696. addr := os.Getenv("TEST_KDC_ADDR")
  697. if addr == "" {
  698. addr = testdata.TEST_KDC_ADDR
  699. }
  700. b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
  701. kt, _ := keytab.Parse(b)
  702. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  703. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_SHORTTICKETS}
  704. cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
  705. cl.WithConfig(c)
  706. err := cl.Login()
  707. if err != nil {
  708. t.Fatalf("error on login: %v\n", err)
  709. }
  710. spn := "HTTP/host.test.gokrb5"
  711. _, _, err = cl.GetServiceTicket(spn)
  712. if err != nil {
  713. t.Fatalf("error getting service ticket: %v\n", err)
  714. }
  715. n := runtime.NumGoroutine()
  716. time.Sleep(time.Second * 60)
  717. cl.Destroy()
  718. time.Sleep(time.Second * 5)
  719. assert.True(t, runtime.NumGoroutine() < n, "auto-renewal goroutine was not stopped when client destroyed")
  720. is, _ := cl.IsConfigured()
  721. assert.False(t, is, "client is still configured after it was destroyed")
  722. }