1 /*
2   This is free and unencumbered software released into the public domain.
3 
4   Anyone is free to copy, modify, publish, use, compile, sell, or
5   distribute this software, either in source code form or as a compiled
6   binary, for any purpose, commercial or non-commercial, and by any
7   means.
8 
9   In jurisdictions that recognize copyright laws, the author or authors
10   of this software dedicate any and all copyright interest in the
11   software to the public domain. We make this dedication for the benefit
12   of the public at large and to the detriment of our heirs and
13   successors. We intend this dedication to be an overt act of
14   relinquishment in perpetuity of all present and future rights to this
15   software under copyright law.
16 
17   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23   OTHER DEALINGS IN THE SOFTWARE.
24 
25   For more information, please refer to <http://unlicense.org/>
26 */
27 
28 #include <dse.h>
29 #include <stdio.h>
30 
31 /*
32  * This example program assumes that the following setup is done in DSE apriori:
33  *
34  * 1. DSE is configured to authenticate with Kerberos.
35  * 2. Using cqlsh as an administrator user (e.g. cassandra), create the following
36  *    objects and grant permissions for them:
37  *
38       CREATE ROLE target_user WITH PASSWORD = 'target_user' and LOGIN = true;
39       CREATE KEYSPACE examples WITH REPLICATION = {'class': 'SimpleStrategy', 'replication_factor':
40  1}; CREATE TABLE examples.gss_proxy_auth (f1 int PRIMARY KEY, f2 int); INSERT INTO
41  examples.gss_proxy_auth (f1, f2) VALUES (1, 2); GRANT ALL ON examples.gss_proxy_auth TO
42  target_user;
43 
44       GRANT PROXY.LOGIN ON ROLE 'target_user' to 'dseuser@DATASTAX.COM';
45  *
46  * Substitute your own Kerberos user for 'dseuser@DATASTAX.COM' (in the above cql and
47  * the KERBEROS_USER macro below).
48  *
49  * Note that proxy auth can target an internal user (e.g. target_user) even if the authenticated
50  * user is from Kerberos.
51  */
52 
53 #define KERBEROS_USER "dseuser@DATASTAX.COM"
54 
print_error(CassFuture * future)55 void print_error(CassFuture* future) {
56   const char* message;
57   size_t message_length;
58   cass_future_error_message(future, &message, &message_length);
59   fprintf(stderr, "Error: %.*s\n", (int)message_length, message);
60 }
61 
select_and_dump(CassSession * session)62 CassError select_and_dump(CassSession* session) {
63   CassError rc = CASS_OK;
64   CassStatement* statement = NULL;
65   CassFuture* future = NULL;
66   const char* query = "SELECT * FROM examples.gss_proxy_auth";
67 
68   statement = cass_statement_new(query, 0);
69   future = cass_session_execute(session, statement);
70 
71   rc = cass_future_error_code(future);
72   if (rc != CASS_OK) {
73     print_error(future);
74   } else {
75     const CassResult* result = cass_future_get_result(future);
76     CassIterator* iterator = cass_iterator_from_result(result);
77 
78     if (cass_iterator_next(iterator)) {
79       int f1, f2;
80       const CassRow* row = cass_iterator_get_row(iterator);
81       if (cass_value_get_int32(cass_row_get_column(row, 0), &f1) != CASS_OK ||
82           cass_value_get_int32(cass_row_get_column(row, 1), &f2) != CASS_OK) {
83         print_error(future);
84       } else {
85         printf("f1: %d    f2: %d\n", f1, f2);
86       }
87     }
88 
89     cass_result_free(result);
90     cass_iterator_free(iterator);
91   }
92 
93   cass_future_free(future);
94   cass_statement_free(statement);
95 
96   return rc;
97 }
98 
connect_session(CassSession * session,const CassCluster * cluster)99 CassError connect_session(CassSession* session, const CassCluster* cluster) {
100   CassError rc = CASS_OK;
101   CassFuture* future = cass_session_connect(session, cluster);
102 
103   rc = cass_future_error_code(future);
104   if (rc != CASS_OK) {
105     print_error(future);
106   }
107   cass_future_free(future);
108 
109   return rc;
110 }
111 
connect_and_run(const char * hosts,const char * proxy_user)112 void connect_and_run(const char* hosts, const char* proxy_user) {
113   CassCluster* cluster = cass_cluster_new();
114   CassSession* session = cass_session_new();
115 
116   /* Add contact points */
117   cass_cluster_set_contact_points(cluster, hosts);
118 
119   /* Hostname resolution is typically necessary when authenticating with Kerberos. */
120   cass_cluster_set_use_hostname_resolution(cluster, cass_true);
121 
122   /* Authenticate as the Kerberos user. If proxy_user is non-null,
123    * declare that we want to execute all statements as proxy_user. */
124   if (proxy_user == NULL) {
125     cass_cluster_set_dse_gssapi_authenticator(cluster, "dse", KERBEROS_USER);
126   } else {
127     cass_cluster_set_dse_gssapi_authenticator_proxy(cluster, "dse", KERBEROS_USER, "target_user");
128   }
129 
130   if (connect_session(session, cluster) != CASS_OK) {
131     cass_cluster_free(cluster);
132     cass_session_free(session);
133     return;
134   }
135 
136   select_and_dump(session);
137 
138   cass_cluster_free(cluster);
139   cass_session_free(session);
140 }
141 
main(int argc,char * argv[])142 int main(int argc, char* argv[]) {
143   /* Setup and connect to cluster */
144   char* hosts = "127.0.0.1";
145   if (argc > 1) {
146     hosts = argv[1];
147   }
148 
149   /* Enable info logging if desired. */
150   /* cass_log_set_level(CASS_LOG_INFO); */
151 
152   printf("Running a query without a proxy user should fail:\n");
153   connect_and_run(hosts, NULL);
154   printf("\nRunning a query with proxy user 'target_user' should succeed:\n");
155   connect_and_run(hosts, "target_user");
156 
157   return 0;
158 }
159