r22969: fix some more places where we could end up with more than one event
[samba.git] / source4 / auth / credentials / credentials.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    User credentials handling
5
6    Copyright (C) Jelmer Vernooij 2005
7    Copyright (C) Tim Potter 2001
8    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26 #include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
27 #include "auth/credentials/credentials.h"
28 #include "auth/credentials/credentials_krb5.h"
29 #include "libcli/auth/libcli_auth.h"
30 #include "lib/events/events.h"
31
32 /**
33  * Create a new credentials structure
34  * @param mem_ctx TALLOC_CTX parent for credentials structure 
35  */
36 struct cli_credentials *cli_credentials_init(TALLOC_CTX *mem_ctx) 
37 {
38         struct cli_credentials *cred = talloc(mem_ctx, struct cli_credentials);
39         if (!cred) {
40                 return cred;
41         }
42
43         cred->netlogon_creds = NULL;
44         cred->machine_account_pending = False;
45         cred->workstation_obtained = CRED_UNINITIALISED;
46         cred->username_obtained = CRED_UNINITIALISED;
47         cred->password_obtained = CRED_UNINITIALISED;
48         cred->domain_obtained = CRED_UNINITIALISED;
49         cred->realm_obtained = CRED_UNINITIALISED;
50         cred->ccache_obtained = CRED_UNINITIALISED;
51         cred->client_gss_creds_obtained = CRED_UNINITIALISED;
52         cred->server_gss_creds_obtained = CRED_UNINITIALISED;
53         cred->keytab_obtained = CRED_UNINITIALISED;
54         cred->principal_obtained = CRED_UNINITIALISED;
55
56         cred->old_password = NULL;
57         cred->smb_krb5_context = NULL;
58         cred->salt_principal = NULL;
59         cred->machine_account = False;
60
61         cred->bind_dn = NULL;
62
63         cred->tries = 3;
64         cred->callback_running = False;
65         cred->ev = NULL;
66
67         cli_credentials_set_kerberos_state(cred, CRED_AUTO_USE_KERBEROS);
68         cli_credentials_set_gensec_features(cred, 0);
69
70         return cred;
71 }
72
73 /**
74  * Create a new anonymous credential
75  * @param mem_ctx TALLOC_CTX parent for credentials structure 
76  */
77 struct cli_credentials *cli_credentials_init_anon(TALLOC_CTX *mem_ctx) 
78 {
79         struct cli_credentials *anon_credentials;
80
81         anon_credentials = cli_credentials_init(mem_ctx);
82         cli_credentials_set_conf(anon_credentials);
83         cli_credentials_set_anonymous(anon_credentials);
84
85         return anon_credentials;
86 }
87
88 void cli_credentials_set_kerberos_state(struct cli_credentials *creds, 
89                                         enum credentials_use_kerberos use_kerberos)
90 {
91         creds->use_kerberos = use_kerberos;
92 }
93
94 enum credentials_use_kerberos cli_credentials_get_kerberos_state(struct cli_credentials *creds)
95 {
96         return creds->use_kerberos;
97 }
98
99 void cli_credentials_set_gensec_features(struct cli_credentials *creds, uint32_t gensec_features)
100 {
101         creds->gensec_features = gensec_features;
102 }
103
104 uint32_t cli_credentials_get_gensec_features(struct cli_credentials *creds)
105 {
106         return creds->gensec_features;
107 }
108
109
110 /**
111  * Obtain the username for this credentials context.
112  * @param cred credentials context
113  * @retval The username set on this context.
114  * @note Return value will never be NULL except by programmer error.
115  */
116 const char *cli_credentials_get_username(struct cli_credentials *cred)
117 {
118         if (cred->machine_account_pending) {
119                 cli_credentials_set_machine_account(cred);
120         }
121
122         if (cred->username_obtained == CRED_CALLBACK && 
123             !cred->callback_running) {
124                 cred->callback_running = True;
125                 cred->username = cred->username_cb(cred);
126                 cred->callback_running = False;
127                 cred->username_obtained = CRED_SPECIFIED;
128         }
129
130         return cred->username;
131 }
132
133 BOOL cli_credentials_set_username(struct cli_credentials *cred, 
134                                   const char *val, enum credentials_obtained obtained)
135 {
136         if (obtained >= cred->username_obtained) {
137                 cred->username = talloc_strdup(cred, val);
138                 cred->username_obtained = obtained;
139                 return True;
140         }
141
142         return False;
143 }
144
145 BOOL cli_credentials_set_username_callback(struct cli_credentials *cred,
146                                   const char *(*username_cb) (struct cli_credentials *))
147 {
148         if (cred->username_obtained < CRED_CALLBACK) {
149                 cred->username_cb = username_cb;
150                 cred->username_obtained = CRED_CALLBACK;
151                 return True;
152         }
153
154         return False;
155 }
156
157 BOOL cli_credentials_set_bind_dn(struct cli_credentials *cred, 
158                                  const char *bind_dn)
159 {
160         cred->bind_dn = talloc_strdup(cred, bind_dn);
161         return True;
162 }
163
164 /**
165  * Obtain the BIND DN for this credentials context.
166  * @param cred credentials context
167  * @retval The username set on this context.
168  * @note Return value will be NULL if not specified explictly
169  */
170 const char *cli_credentials_get_bind_dn(struct cli_credentials *cred)
171 {
172         return cred->bind_dn;
173 }
174
175
176 /**
177  * Obtain the client principal for this credentials context.
178  * @param cred credentials context
179  * @retval The username set on this context.
180  * @note Return value will never be NULL except by programmer error.
181  */
182 const char *cli_credentials_get_principal(struct cli_credentials *cred, TALLOC_CTX *mem_ctx)
183 {
184         if (cred->machine_account_pending) {
185                 cli_credentials_set_machine_account(cred);
186         }
187
188         if (cred->principal_obtained == CRED_CALLBACK && 
189             !cred->callback_running) {
190                 cred->callback_running = True;
191                 cred->principal = cred->principal_cb(cred);
192                 cred->callback_running = False;
193                 cred->principal_obtained = CRED_SPECIFIED;
194         }
195
196         if (cred->principal_obtained < cred->username_obtained) {
197                 if (cred->domain_obtained > cred->realm_obtained) {
198                         return talloc_asprintf(mem_ctx, "%s@%s", 
199                                                cli_credentials_get_username(cred),
200                                                cli_credentials_get_domain(cred));
201                 } else {
202                         return talloc_asprintf(mem_ctx, "%s@%s", 
203                                                cli_credentials_get_username(cred),
204                                                cli_credentials_get_realm(cred));
205                 }
206         }
207         return talloc_reference(mem_ctx, cred->principal);
208 }
209
210 BOOL cli_credentials_set_principal(struct cli_credentials *cred, 
211                                    const char *val, 
212                                    enum credentials_obtained obtained)
213 {
214         if (obtained >= cred->principal_obtained) {
215                 cred->principal = talloc_strdup(cred, val);
216                 cred->principal_obtained = obtained;
217                 return True;
218         }
219
220         return False;
221 }
222
223 /* Set a callback to get the principal.  This could be a popup dialog,
224  * a terminal prompt or similar.  */
225
226 BOOL cli_credentials_set_principal_callback(struct cli_credentials *cred,
227                                   const char *(*principal_cb) (struct cli_credentials *))
228 {
229         if (cred->principal_obtained < CRED_CALLBACK) {
230                 cred->principal_cb = principal_cb;
231                 cred->principal_obtained = CRED_CALLBACK;
232                 return True;
233         }
234
235         return False;
236 }
237
238 /* Some of our tools are 'anonymous by default'.  This is a single
239  * function to determine if authentication has been explicitly
240  * requested */
241
242 BOOL cli_credentials_authentication_requested(struct cli_credentials *cred) 
243 {
244         if (cred->bind_dn) {
245                 return True;
246         }
247
248         if (cli_credentials_is_anonymous(cred)){
249                 return False;
250         }
251
252         if (cred->principal_obtained >= CRED_SPECIFIED) {
253                 return True;
254         }
255         if (cred->username_obtained >= CRED_SPECIFIED) {
256                 return True;
257         }
258
259         if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
260                 return True;
261         }
262
263         return False;
264 }
265
266 /**
267  * Obtain the password for this credentials context.
268  * @param cred credentials context
269  * @retval If set, the cleartext password, otherwise NULL
270  */
271 const char *cli_credentials_get_password(struct cli_credentials *cred)
272 {
273         if (cred->machine_account_pending) {
274                 cli_credentials_set_machine_account(cred);
275         }
276
277         if (cred->password_obtained == CRED_CALLBACK && 
278             !cred->callback_running) {
279                 cred->callback_running = True;
280                 cred->password = cred->password_cb(cred);
281                 cred->callback_running = False;
282                 cred->password_obtained = CRED_CALLBACK_RESULT;
283         }
284
285         return cred->password;
286 }
287
288 /* Set a password on the credentials context, including an indication
289  * of 'how' the password was obtained */
290
291 BOOL cli_credentials_set_password(struct cli_credentials *cred, 
292                                   const char *val, 
293                                   enum credentials_obtained obtained)
294 {
295         if (obtained >= cred->password_obtained) {
296                 cred->password = talloc_strdup(cred, val);
297                 cred->password_obtained = obtained;
298
299                 cred->nt_hash = NULL;
300                 return True;
301         }
302
303         return False;
304 }
305
306 BOOL cli_credentials_set_password_callback(struct cli_credentials *cred,
307                                            const char *(*password_cb) (struct cli_credentials *))
308 {
309         if (cred->password_obtained < CRED_CALLBACK) {
310                 cred->password_cb = password_cb;
311                 cred->password_obtained = CRED_CALLBACK;
312                 return True;
313         }
314
315         return False;
316 }
317
318 /**
319  * Obtain the 'old' password for this credentials context (used for join accounts).
320  * @param cred credentials context
321  * @retval If set, the cleartext password, otherwise NULL
322  */
323 const char *cli_credentials_get_old_password(struct cli_credentials *cred)
324 {
325         if (cred->machine_account_pending) {
326                 cli_credentials_set_machine_account(cred);
327         }
328
329         return cred->old_password;
330 }
331
332 BOOL cli_credentials_set_old_password(struct cli_credentials *cred, 
333                                       const char *val, 
334                                       enum credentials_obtained obtained)
335 {
336         cred->old_password = talloc_strdup(cred, val);
337         return True;
338 }
339
340 /**
341  * Obtain the password, in the form MD4(unicode(password)) for this credentials context.
342  *
343  * Sometimes we only have this much of the password, while the rest of
344  * the time this call avoids calling E_md4hash themselves.
345  *
346  * @param cred credentials context
347  * @retval If set, the cleartext password, otherwise NULL
348  */
349 const struct samr_Password *cli_credentials_get_nt_hash(struct cli_credentials *cred, 
350                                                         TALLOC_CTX *mem_ctx)
351 {
352         const char *password = cli_credentials_get_password(cred);
353
354         if (password) {
355                 struct samr_Password *nt_hash = talloc(mem_ctx, struct samr_Password);
356                 if (!nt_hash) {
357                         return NULL;
358                 }
359                 
360                 E_md4hash(password, nt_hash->hash);    
361
362                 return nt_hash;
363         } else {
364                 return cred->nt_hash;
365         }
366 }
367
368 BOOL cli_credentials_set_nt_hash(struct cli_credentials *cred,
369                                  const struct samr_Password *nt_hash, 
370                                  enum credentials_obtained obtained)
371 {
372         if (obtained >= cred->password_obtained) {
373                 cli_credentials_set_password(cred, NULL, obtained);
374                 if (nt_hash) {
375                         cred->nt_hash = talloc(cred, struct samr_Password);
376                         *cred->nt_hash = *nt_hash;
377                 } else {
378                         cred->nt_hash = NULL;
379                 }
380                 return True;
381         }
382
383         return False;
384 }
385
386 /**
387  * Obtain the 'short' or 'NetBIOS' domain for this credentials context.
388  * @param cred credentials context
389  * @retval The domain set on this context. 
390  * @note Return value will never be NULL except by programmer error.
391  */
392 const char *cli_credentials_get_domain(struct cli_credentials *cred)
393 {
394         if (cred->machine_account_pending) {
395                 cli_credentials_set_machine_account(cred);
396         }
397
398         if (cred->domain_obtained == CRED_CALLBACK && 
399             !cred->callback_running) {
400                 cred->callback_running = True;
401                 cred->domain = cred->domain_cb(cred);
402                 cred->callback_running = False;
403                 cred->domain_obtained = CRED_SPECIFIED;
404         }
405
406         return cred->domain;
407 }
408
409
410 BOOL cli_credentials_set_domain(struct cli_credentials *cred, 
411                                 const char *val, 
412                                 enum credentials_obtained obtained)
413 {
414         if (obtained >= cred->domain_obtained) {
415                 /* it is important that the domain be in upper case,
416                  * particularly for the sensitive NTLMv2
417                  * calculations */
418                 cred->domain = strupper_talloc(cred, val);
419                 cred->domain_obtained = obtained;
420                 return True;
421         }
422
423         return False;
424 }
425
426 BOOL cli_credentials_set_domain_callback(struct cli_credentials *cred,
427                                          const char *(*domain_cb) (struct cli_credentials *))
428 {
429         if (cred->domain_obtained < CRED_CALLBACK) {
430                 cred->domain_cb = domain_cb;
431                 cred->domain_obtained = CRED_CALLBACK;
432                 return True;
433         }
434
435         return False;
436 }
437
438 /**
439  * Obtain the Kerberos realm for this credentials context.
440  * @param cred credentials context
441  * @retval The realm set on this context. 
442  * @note Return value will never be NULL except by programmer error.
443  */
444 const char *cli_credentials_get_realm(struct cli_credentials *cred)
445 {       
446         if (cred->machine_account_pending) {
447                 cli_credentials_set_machine_account(cred);
448         }
449
450         if (cred->realm_obtained == CRED_CALLBACK && 
451             !cred->callback_running) {
452                 cred->callback_running = True;
453                 cred->realm = cred->realm_cb(cred);
454                 cred->callback_running = False;
455                 cred->realm_obtained = CRED_SPECIFIED;
456         }
457
458         return cred->realm;
459 }
460
461 /**
462  * Set the realm for this credentials context, and force it to
463  * uppercase for the sainity of our local kerberos libraries 
464  */
465 BOOL cli_credentials_set_realm(struct cli_credentials *cred, 
466                                const char *val, 
467                                enum credentials_obtained obtained)
468 {
469         if (obtained >= cred->realm_obtained) {
470                 cred->realm = strupper_talloc(cred, val);
471                 cred->realm_obtained = obtained;
472                 return True;
473         }
474
475         return False;
476 }
477
478 BOOL cli_credentials_set_realm_callback(struct cli_credentials *cred,
479                                         const char *(*realm_cb) (struct cli_credentials *))
480 {
481         if (cred->realm_obtained < CRED_CALLBACK) {
482                 cred->realm_cb = realm_cb;
483                 cred->realm_obtained = CRED_CALLBACK;
484                 return True;
485         }
486
487         return False;
488 }
489
490 /**
491  * Obtain the 'short' or 'NetBIOS' workstation name for this credentials context.
492  *
493  * @param cred credentials context
494  * @retval The workstation name set on this context. 
495  * @note Return value will never be NULL except by programmer error.
496  */
497 const char *cli_credentials_get_workstation(struct cli_credentials *cred)
498 {
499         if (cred->workstation_obtained == CRED_CALLBACK && 
500             !cred->callback_running) {
501                 cred->callback_running = True;
502                 cred->workstation = cred->workstation_cb(cred);
503                 cred->callback_running = False;
504                 cred->workstation_obtained = CRED_SPECIFIED;
505         }
506
507         return cred->workstation;
508 }
509
510 BOOL cli_credentials_set_workstation(struct cli_credentials *cred, 
511                                      const char *val, 
512                                      enum credentials_obtained obtained)
513 {
514         if (obtained >= cred->workstation_obtained) {
515                 cred->workstation = talloc_strdup(cred, val);
516                 cred->workstation_obtained = obtained;
517                 return True;
518         }
519
520         return False;
521 }
522
523 BOOL cli_credentials_set_workstation_callback(struct cli_credentials *cred,
524                                               const char *(*workstation_cb) (struct cli_credentials *))
525 {
526         if (cred->workstation_obtained < CRED_CALLBACK) {
527                 cred->workstation_cb = workstation_cb;
528                 cred->workstation_obtained = CRED_CALLBACK;
529                 return True;
530         }
531
532         return False;
533 }
534
535 /**
536  * Given a string, typically obtained from a -U argument, parse it into domain, username, realm and password fields
537  *
538  * The format accepted is [domain\\]user[%password] or user[@realm][%password]
539  *
540  * @param credentials Credentials structure on which to set the password
541  * @param data the string containing the username, password etc
542  * @param obtained This enum describes how 'specified' this password is
543  */
544
545 void cli_credentials_parse_string(struct cli_credentials *credentials, const char *data, enum credentials_obtained obtained)
546 {
547         char *uname, *p;
548
549         if (strcmp("%",data) == 0) {
550                 cli_credentials_set_anonymous(credentials);
551                 return;
552         }
553
554         uname = talloc_strdup(credentials, data); 
555         if ((p = strchr_m(uname,'%'))) {
556                 *p = 0;
557                 cli_credentials_set_password(credentials, p+1, obtained);
558         }
559
560         if ((p = strchr_m(uname,'@'))) {
561                 cli_credentials_set_principal(credentials, uname, obtained);
562                 *p = 0;
563                 cli_credentials_set_realm(credentials, p+1, obtained);
564                 return;
565         } else if ((p = strchr_m(uname,'\\')) || (p = strchr_m(uname, '/'))) {
566                 *p = 0;
567                 cli_credentials_set_domain(credentials, uname, obtained);
568                 uname = p+1;
569         }
570         cli_credentials_set_username(credentials, uname, obtained);
571 }
572
573 /**
574  * Given a a credentials structure, print it as a string
575  *
576  * The format output is [domain\\]user[%password] or user[@realm][%password]
577  *
578  * @param credentials Credentials structure on which to set the password
579  * @param mem_ctx The memory context to place the result on
580  */
581
582 const char *cli_credentials_get_unparsed_name(struct cli_credentials *credentials, TALLOC_CTX *mem_ctx)
583 {
584         const char *bind_dn = cli_credentials_get_bind_dn(credentials);
585         const char *domain;
586         const char *username;
587         const char *name;
588
589         if (bind_dn) {
590                 name = talloc_reference(mem_ctx, bind_dn);
591         } else {
592                 cli_credentials_get_ntlm_username_domain(credentials, mem_ctx, &username, &domain);
593                 if (domain && domain[0]) {
594                         name = talloc_asprintf(mem_ctx, "%s\\%s", 
595                                                domain, username);
596                 } else {
597                         name = talloc_asprintf(mem_ctx, "%s", 
598                                                username);
599                 }
600         }
601         return name;
602 }
603
604 /**
605  * Specifies default values for domain, workstation and realm
606  * from the smb.conf configuration file
607  *
608  * @param cred Credentials structure to fill in
609  */
610 void cli_credentials_set_conf(struct cli_credentials *cred)
611 {
612         cli_credentials_set_username(cred, "", CRED_UNINITIALISED);
613         cli_credentials_set_domain(cred, lp_workgroup(), CRED_UNINITIALISED);
614         cli_credentials_set_workstation(cred, lp_netbios_name(), CRED_UNINITIALISED);
615         cli_credentials_set_realm(cred, lp_realm(), CRED_UNINITIALISED);
616 }
617
618 /**
619  * Guess defaults for credentials from environment variables, 
620  * and from the configuration file
621  * 
622  * @param cred Credentials structure to fill in
623  */
624 void cli_credentials_guess(struct cli_credentials *cred)
625 {
626         char *p;
627
628         cli_credentials_set_conf(cred);
629         
630         if (getenv("LOGNAME")) {
631                 cli_credentials_set_username(cred, getenv("LOGNAME"), CRED_GUESS_ENV);
632         }
633
634         if (getenv("USER")) {
635                 cli_credentials_parse_string(cred, getenv("USER"), CRED_GUESS_ENV);
636                 if ((p = strchr_m(getenv("USER"),'%'))) {
637                         memset(p,0,strlen(cred->password));
638                 }
639         }
640
641         if (getenv("PASSWD")) {
642                 cli_credentials_set_password(cred, getenv("PASSWD"), CRED_GUESS_ENV);
643         }
644
645         if (getenv("PASSWD_FD")) {
646                 cli_credentials_parse_password_fd(cred, atoi(getenv("PASSWD_FD")), CRED_GUESS_FILE);
647         }
648         
649         p = getenv("PASSWD_FILE");
650         if (p && p[0]) {
651                 cli_credentials_parse_password_file(cred, p, CRED_GUESS_FILE);
652         }
653         
654         if (cli_credentials_get_kerberos_state(cred) != CRED_DONT_USE_KERBEROS) {
655                 cli_credentials_set_ccache(cred, NULL, CRED_GUESS_FILE);
656         }
657 }
658
659 /**
660  * Attach NETLOGON credentials for use with SCHANNEL
661  */
662
663 void cli_credentials_set_netlogon_creds(struct cli_credentials *cred, 
664                                         struct creds_CredentialState *netlogon_creds)
665 {
666         cred->netlogon_creds = talloc_reference(cred, netlogon_creds);
667 }
668
669 /**
670  * Return attached NETLOGON credentials 
671  */
672
673 struct creds_CredentialState *cli_credentials_get_netlogon_creds(struct cli_credentials *cred)
674 {
675         return cred->netlogon_creds;
676 }
677
678 /** 
679  * Set NETLOGON secure channel type
680  */
681
682 void cli_credentials_set_secure_channel_type(struct cli_credentials *cred,
683                                              enum netr_SchannelType secure_channel_type)
684 {
685         cred->secure_channel_type = secure_channel_type;
686 }
687
688 /**
689  * Return NETLOGON secure chanel type
690  */
691
692 enum netr_SchannelType cli_credentials_get_secure_channel_type(struct cli_credentials *cred)
693 {
694         return cred->secure_channel_type;
695 }
696
697 /**
698  * Fill in a credentials structure as the anonymous user
699  */
700 void cli_credentials_set_anonymous(struct cli_credentials *cred) 
701 {
702         cli_credentials_set_username(cred, "", CRED_SPECIFIED);
703         cli_credentials_set_domain(cred, "", CRED_SPECIFIED);
704         cli_credentials_set_password(cred, NULL, CRED_SPECIFIED);
705 }
706
707 /**
708  * Describe a credentials context as anonymous or authenticated
709  * @retval True if anonymous, False if a username is specified
710  */
711
712 BOOL cli_credentials_is_anonymous(struct cli_credentials *cred)
713 {
714         const char *username;
715         
716         if (cred->machine_account_pending) {
717                 cli_credentials_set_machine_account(cred);
718         }
719
720         username = cli_credentials_get_username(cred);
721         
722         /* Yes, it is deliberate that we die if we have a NULL pointer
723          * here - anonymous is "", not NULL, which is 'never specified,
724          * never guessed', ie programmer bug */
725         if (!username[0]) {
726                 return True;
727         }
728
729         return False;
730 }
731
732 /**
733  * Mark the current password for a credentials struct as wrong. This will 
734  * cause the password to be prompted again (if a callback is set).
735  *
736  * This will decrement the number of times the password can be tried.
737  *
738  * @retval whether the credentials struct is finished
739  */
740 BOOL cli_credentials_wrong_password(struct cli_credentials *cred)
741 {
742         if (cred->password_obtained != CRED_CALLBACK_RESULT) {
743                 return False;
744         }
745         
746         cred->password_obtained = CRED_CALLBACK;
747
748         cred->tries--;
749
750         return (cred->tries > 0);
751 }
752
753 /*
754   set the common event context for this set of credentials
755  */
756 void cli_credentials_set_event_context(struct cli_credentials *cred, struct event_context *ev)
757 {
758         cred->ev = ev;
759 }
760
761 /*
762   set the common event context for this set of credentials
763  */
764 struct event_context *cli_credentials_get_event_context(struct cli_credentials *cred)
765 {
766         if (cred->ev == NULL) {
767                 cred->ev = event_context_find(cred);
768         }
769         return cred->ev;
770 }