client_integration_test.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  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, err := cl.sessionFromRealm("TEST.GOKRB5")
  385. if err != nil {
  386. t.Fatalf("error initially getting session: %v", err)
  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. s, err := cl.sessionFromRealm("TEST.GOKRB5")
  403. if err != nil {
  404. t.Logf("error getting session: %v", err)
  405. }
  406. fmt.Fprintf(ioutil.Discard, "%v", s.renewTill)
  407. }()
  408. time.Sleep(time.Second)
  409. }
  410. wg.Wait()
  411. }
  412. func spnegoGet(cl *Client) error {
  413. url := os.Getenv("TEST_HTTP_URL")
  414. if url == "" {
  415. url = testdata.TEST_HTTP_URL
  416. }
  417. r, _ := http.NewRequest("GET", url+"/modgssapi/index.html", nil)
  418. httpResp, err := http.DefaultClient.Do(r)
  419. if err != nil {
  420. return fmt.Errorf("request error: %v\n", err)
  421. }
  422. if httpResp.StatusCode != http.StatusUnauthorized {
  423. return errors.New("did not get unauthorized code when no SPNEGO header set")
  424. }
  425. err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5")
  426. if err != nil {
  427. return fmt.Errorf("error setting client SPNEGO header: %v", err)
  428. }
  429. httpResp, err = http.DefaultClient.Do(r)
  430. if err != nil {
  431. return fmt.Errorf("request error: %v\n", err)
  432. }
  433. if httpResp.StatusCode != http.StatusOK {
  434. return errors.New("did not get OK code when SPNEGO header set")
  435. }
  436. return nil
  437. }
  438. func TestNewClientFromCCache(t *testing.T) {
  439. b, err := hex.DecodeString(testdata.CCACHE_TEST)
  440. if err != nil {
  441. t.Fatalf("error decoding test data")
  442. }
  443. cc, err := credentials.ParseCCache(b)
  444. if err != nil {
  445. t.Fatal("error getting test CCache")
  446. }
  447. cl, err := NewClientFromCCache(cc)
  448. if err != nil {
  449. t.Fatalf("error creating client from CCache: %v", err)
  450. }
  451. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  452. addr := os.Getenv("TEST_KDC_ADDR")
  453. if addr == "" {
  454. addr = testdata.TEST_KDC_ADDR
  455. }
  456. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
  457. cl.WithConfig(c)
  458. if ok, err := cl.IsConfigured(); !ok {
  459. t.Fatalf("client was not configured from CCache: %v", err)
  460. }
  461. }
  462. // Login to the TEST.GOKRB5 domain and request service ticket for resource in the RESDOM.GOKRB5 domain.
  463. // There is a trust between the two domains.
  464. func TestClient_GetServiceTicket_Trusted_Resource_Domain(t *testing.T) {
  465. b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
  466. kt, _ := keytab.Parse(b)
  467. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  468. addr := os.Getenv("TEST_KDC_ADDR")
  469. if addr == "" {
  470. addr = testdata.TEST_KDC_ADDR
  471. }
  472. for i, r := range c.Realms {
  473. if r.Realm == "TEST.GOKRB5" {
  474. c.Realms[i].KDC = []string{addr + ":" + testdata.TEST_KDC}
  475. }
  476. if r.Realm == "RESDOM.GOKRB5" {
  477. c.Realms[i].KDC = []string{addr + ":" + testdata.TEST_KDC_RESDOM}
  478. }
  479. }
  480. c.LibDefaults.DefaultRealm = "TEST.GOKRB5"
  481. cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
  482. c.LibDefaults.DefaultTktEnctypes = []string{"aes256-cts-hmac-sha1-96"}
  483. c.LibDefaults.DefaultTktEnctypeIDs = []int32{etypeID.ETypesByName["aes256-cts-hmac-sha1-96"]}
  484. c.LibDefaults.DefaultTGSEnctypes = []string{"aes256-cts-hmac-sha1-96"}
  485. c.LibDefaults.DefaultTGSEnctypeIDs = []int32{etypeID.ETypesByName["aes256-cts-hmac-sha1-96"]}
  486. cl.WithConfig(c)
  487. err := cl.Login()
  488. if err != nil {
  489. t.Fatalf("error on login: %v\n", err)
  490. }
  491. spn := "HTTP/host.resdom.gokrb5"
  492. tkt, key, err := cl.GetServiceTicket(spn)
  493. if err != nil {
  494. t.Fatalf("error getting service ticket: %v\n", err)
  495. }
  496. assert.Equal(t, spn, tkt.SName.GetPrincipalNameString())
  497. assert.Equal(t, etypeID.ETypesByName["aes256-cts-hmac-sha1-96"], key.KeyType)
  498. b, _ = hex.DecodeString(testdata.SYSHTTP_RESDOM_KEYTAB)
  499. skt, _ := keytab.Parse(b)
  500. err = tkt.DecryptEncPart(skt, "")
  501. if err != nil {
  502. t.Errorf("error decrypting ticket with service keytab: %v", err)
  503. }
  504. }
  505. const (
  506. kinitCmd = "kinit"
  507. kvnoCmd = "kvno"
  508. spn = "HTTP/host.test.gokrb5"
  509. )
  510. func login() error {
  511. file, err := os.Create("/etc/krb5.conf")
  512. if err != nil {
  513. return fmt.Errorf("cannot open krb5.conf: %v", err)
  514. }
  515. defer file.Close()
  516. fmt.Fprintf(file, testdata.TEST_KRB5CONF)
  517. cmd := exec.Command(kinitCmd, "testuser1@TEST.GOKRB5")
  518. stdinR, stdinW := io.Pipe()
  519. stderrR, stderrW := io.Pipe()
  520. cmd.Stdin = stdinR
  521. cmd.Stderr = stderrW
  522. err = cmd.Start()
  523. if err != nil {
  524. return fmt.Errorf("could not start %s command: %v", kinitCmd, err)
  525. }
  526. go func() {
  527. io.WriteString(stdinW, "passwordvalue")
  528. stdinW.Close()
  529. }()
  530. errBuf := new(bytes.Buffer)
  531. go func() {
  532. io.Copy(errBuf, stderrR)
  533. stderrR.Close()
  534. }()
  535. err = cmd.Wait()
  536. if err != nil {
  537. return fmt.Errorf("%s did not run successfully: %v stderr: %s", kinitCmd, err, string(errBuf.Bytes()))
  538. }
  539. return nil
  540. }
  541. func getServiceTkt() error {
  542. cmd := exec.Command(kvnoCmd, spn)
  543. err := cmd.Start()
  544. if err != nil {
  545. return fmt.Errorf("could not start %s command: %v", kvnoCmd, err)
  546. }
  547. err = cmd.Wait()
  548. if err != nil {
  549. return fmt.Errorf("%s did not run successfully: %v", kvnoCmd, err)
  550. }
  551. return nil
  552. }
  553. func loadCCache() (credentials.CCache, error) {
  554. usr, _ := user.Current()
  555. cpath := "/tmp/krb5cc_" + usr.Uid
  556. return credentials.LoadCCache(cpath)
  557. }
  558. func TestGetServiceTicketFromCCacheTGT(t *testing.T) {
  559. err := login()
  560. if err != nil {
  561. t.Fatalf("error logging in with kinit: %v", err)
  562. }
  563. c, err := loadCCache()
  564. if err != nil {
  565. t.Errorf("error loading CCache: %v", err)
  566. }
  567. cfg, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  568. addr := os.Getenv("TEST_KDC_ADDR")
  569. if addr == "" {
  570. addr = testdata.TEST_KDC_ADDR
  571. }
  572. cfg.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
  573. cl, err := NewClientFromCCache(c)
  574. if err != nil {
  575. t.Fatalf("error generating client from ccache: %v", err)
  576. }
  577. cl.WithConfig(cfg)
  578. url := os.Getenv("TEST_HTTP_URL")
  579. if url == "" {
  580. url = testdata.TEST_HTTP_URL
  581. }
  582. r, _ := http.NewRequest("GET", url+"/modgssapi/index.html", nil)
  583. err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5")
  584. if err != nil {
  585. t.Fatalf("error setting client SPNEGO header: %v", err)
  586. }
  587. httpResp, err := http.DefaultClient.Do(r)
  588. if err != nil {
  589. t.Fatalf("request error: %v\n", err)
  590. }
  591. assert.Equal(t, http.StatusOK, httpResp.StatusCode, "status code in response to client SPNEGO request not as expected")
  592. }
  593. func TestGetServiceTicketFromCCacheWithoutKDC(t *testing.T) {
  594. err := login()
  595. if err != nil {
  596. t.Fatalf("error logging in with kinit: %v", err)
  597. }
  598. err = getServiceTkt()
  599. if err != nil {
  600. t.Fatalf("error getting service ticket: %v", err)
  601. }
  602. c, err := loadCCache()
  603. if err != nil {
  604. t.Errorf("error loading CCache: %v", err)
  605. }
  606. cfg, _ := config.NewConfigFromString("...")
  607. cl, err := NewClientFromCCache(c)
  608. if err != nil {
  609. t.Fatalf("error generating client from ccache: %v", err)
  610. }
  611. cl.WithConfig(cfg)
  612. url := os.Getenv("TEST_HTTP_URL")
  613. if url == "" {
  614. url = testdata.TEST_HTTP_URL
  615. }
  616. r, _ := http.NewRequest("GET", url+"/modgssapi/index.html", nil)
  617. err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5")
  618. if err != nil {
  619. t.Fatalf("error setting client SPNEGO header: %v", err)
  620. }
  621. httpResp, err := http.DefaultClient.Do(r)
  622. if err != nil {
  623. t.Fatalf("request error: %v\n", err)
  624. }
  625. assert.Equal(t, http.StatusOK, httpResp.StatusCode, "status code in response to client SPNEGO request not as expected")
  626. }
  627. func TestClient_ChangePasswd(t *testing.T) {
  628. b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
  629. kt, _ := keytab.Parse(b)
  630. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  631. addr := os.Getenv("TEST_KDC_ADDR")
  632. if addr == "" {
  633. addr = testdata.TEST_KDC_ADDR
  634. }
  635. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
  636. c.Realms[0].KPasswdServer = []string{addr + ":464"}
  637. cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
  638. cl.WithConfig(c)
  639. ok, err := cl.ChangePasswd("newpassword")
  640. if err != nil {
  641. t.Fatalf("error changing password: %v", err)
  642. }
  643. assert.True(t, ok, "password was not changed")
  644. cl = NewClientWithPassword("testuser1", "TEST.GOKRB5", "newpassword")
  645. cl.WithConfig(c)
  646. ok, err = cl.ChangePasswd(testdata.TESTUSER1_PASSWORD)
  647. if err != nil {
  648. t.Fatalf("error changing password: %v", err)
  649. }
  650. assert.True(t, ok, "password was not changed back")
  651. }
  652. func TestClient_AutoRenew_Goroutine(t *testing.T) {
  653. // Tests that the auto renew of client credentials is not spawning goroutines out of control.
  654. addr := os.Getenv("TEST_KDC_ADDR")
  655. if addr == "" {
  656. addr = testdata.TEST_KDC_ADDR
  657. }
  658. b, _ := hex.DecodeString(testdata.TESTUSER2_KEYTAB)
  659. kt, _ := keytab.Parse(b)
  660. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  661. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_SHORTTICKETS}
  662. 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.
  663. cl := NewClientWithKeytab("testuser2", "TEST.GOKRB5", kt)
  664. cl.WithConfig(c)
  665. err := cl.Login()
  666. if err != nil {
  667. t.Errorf("error on logging in: %v\n", err)
  668. }
  669. n := runtime.NumGoroutine()
  670. for i := 0; i < 6; i++ {
  671. time.Sleep(time.Second * 20)
  672. sess, err := cl.sessionFromRealm("TEST.GOKRB5")
  673. if err != nil {
  674. t.Errorf("could not get client's session: %v", err)
  675. }
  676. if !sess.valid() {
  677. t.Fatalf("session auto update failed")
  678. }
  679. if runtime.NumGoroutine() > n {
  680. t.Fatalf("number of goroutines is increasing: should not be more than %d, is %d", n, runtime.NumGoroutine())
  681. }
  682. }
  683. }
  684. func TestClient_Destroy(t *testing.T) {
  685. addr := os.Getenv("TEST_KDC_ADDR")
  686. if addr == "" {
  687. addr = testdata.TEST_KDC_ADDR
  688. }
  689. b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
  690. kt, _ := keytab.Parse(b)
  691. c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
  692. c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_SHORTTICKETS}
  693. cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
  694. cl.WithConfig(c)
  695. err := cl.Login()
  696. if err != nil {
  697. t.Fatalf("error on login: %v\n", err)
  698. }
  699. spn := "HTTP/host.test.gokrb5"
  700. _, _, err = cl.GetServiceTicket(spn)
  701. if err != nil {
  702. t.Fatalf("error getting service ticket: %v\n", err)
  703. }
  704. n := runtime.NumGoroutine()
  705. time.Sleep(time.Second * 60)
  706. cl.Destroy()
  707. time.Sleep(time.Second * 5)
  708. assert.True(t, runtime.NumGoroutine() < n, "auto-renewal goroutine was not stopped when client destroyed")
  709. is, _ := cl.IsConfigured()
  710. assert.False(t, is, "client is still configured after it was destroyed")
  711. }